├── .dockerignore ├── .github └── workflows │ └── main.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── circuits ├── auth │ └── authV3.circom ├── authV3.circom ├── credentialAtomicQueryV3.circom ├── credentialAtomicQueryV3OnChain.circom ├── lib │ ├── idOwnership.circom │ ├── linked │ │ └── linkId.circom │ ├── query │ │ ├── comparators.circom │ │ ├── modifiers.circom │ │ ├── processQueryWithModifiers.circom │ │ └── query.circom │ ├── stateTransition.circom │ └── utils │ │ ├── arraySizeValidator.circom │ │ ├── babyjubjub.circom │ │ ├── claimUtils.circom │ │ ├── idUtils.circom │ │ ├── nullify.circom │ │ ├── queryHash.circom │ │ ├── safeOne.circom │ │ ├── spongeHash.circom │ │ ├── tags-managing.circom │ │ └── treeUtils.circom ├── linked │ ├── multiQuery.circom │ └── nullifier.circom ├── linkedMultiQuery10.circom ├── offchain │ └── credentialAtomicQueryV3OffChain.circom ├── onchain │ └── credentialAtomicQueryV3OnChain.circom └── stateTransition.circom ├── compile-circuit.sh ├── generate.sh ├── package-lock.json ├── package.json ├── s3_util.js ├── scripts └── bulk_rename_text_occurrences.sh ├── test ├── auth │ └── authV3.test.ts ├── circuits │ ├── authV3Test.circom │ ├── comparators.circom │ ├── comparators_greater_than.circom │ ├── eddsaposeidon.circom │ ├── eq.circom │ ├── idUtils_CalculateIdChecksum.circom │ ├── idUtils_GatherID.circom │ ├── idUtils_NewID.circom │ ├── idUtils_ProfileID.circom │ ├── idUtils_SelectProfile.circom │ ├── idUtils_SplitID.circom │ ├── idUtils_TakeNBits.circom │ ├── lessthan.circom │ ├── poseidon.circom │ ├── poseidon14.circom │ ├── poseidon16.circom │ ├── query │ │ ├── inTest.circom │ │ ├── nullifyTest.circom │ │ └── queryTest.circom │ ├── stateTransitionTest.circom │ └── utils │ │ ├── claimUtils_getClaimHeader.circom │ │ ├── claimUtils_getClaimMerklizeRoot.circom │ │ ├── utils_GetValueByIndex.circom │ │ ├── utils_arraySizeValidatorTest.circom │ │ ├── utils_checkIdenStateMatchesRoots.circom │ │ ├── utils_getClaimExpiration.circom │ │ ├── utils_getClaimSubjectOtherIden.circom │ │ ├── utils_getSubjectLocation.circom │ │ ├── utils_isExpirable.circom │ │ ├── utils_isUpdatable.circom │ │ ├── utils_verifyClaimSignature.circom │ │ ├── utils_verifyClaimsTreeRoot.circom │ │ ├── utils_verifyCredentialMtp.circom │ │ ├── utils_verifyCredentialMtpHiHv.circom │ │ ├── utils_verifyCredentialNotRevoked.circom │ │ └── utils_verifyExpirationTime.circom ├── comparators.test.ts ├── eddsaposeidon.test.ts ├── linked │ └── linked.test.ts ├── negativeNumbers.test.ts ├── offchain │ └── credentialAtomicQueryV3OffChain.test.ts ├── onchain │ └── credentialAtomicQueryV3OnChain.test.ts ├── poseidon.test.ts ├── query │ ├── in.test.ts │ └── query.test.ts ├── stateTransition.test.ts └── utils │ ├── arraySizeValidator.test.ts │ ├── claimUtils_getClaimHeader.test.ts │ ├── claimUtils_getClaimMerklizeRoot.test.ts │ ├── getClaimExpiration.test.ts │ ├── getClaimSubjectOtherIden.test.ts │ ├── getSubjectLocation.test.ts │ ├── getValueByIndex.test.ts │ ├── idUtils_CalculateIdChecksum.test.ts │ ├── idUtils_GatherID.test.ts │ ├── idUtils_NewID.test.ts │ ├── idUtils_ProfileID.test.ts │ ├── idUtils_SelectProfile.test.ts │ ├── idUtils_SplitID.test.ts │ ├── idUtils_TakeNBits.test.ts │ ├── isExpirable.test.ts │ ├── isUpdatable.test.ts │ ├── nullify.test.ts │ ├── spongeHash.test.ts │ ├── verifyExpirationTime.test.ts │ └── verifyFunctions.test.ts ├── testvectorgen ├── auth │ └── authV3_test.go ├── contract_data │ └── v3_test.go ├── credentials │ ├── linked │ │ └── linked_test.go │ ├── onchain │ │ └── v3 │ │ │ └── v3_test.go │ └── v3 │ │ └── v3_test.go ├── go.mod ├── go.sum ├── statetransition │ └── stateTransition_test.go └── utils │ ├── constants.go │ ├── identity.go │ ├── identity_test.go │ ├── utils.go │ └── w3cSchema.json ├── tsconfig.json ├── tslint.json ├── update-contract-data-v3.js └── update-contract-data.js /.dockerignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .git 3 | .DS_Store 4 | Dockerfile 5 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: 3 | push: 4 | branches: [ '*' ] 5 | pull_request: 6 | branches: [ '*' ] 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: modules-cache 13 | uses: actions/cache@v3 14 | with: 15 | path: '**/node_modules' 16 | key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }} 17 | 18 | - name: Setup other deps 19 | run: sudo apt-get update && sudo apt-get install -y wget nlohmann-json3-dev libgmp-dev nasm g++ build-essential 20 | 21 | - name: Setup Circom 22 | 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 23 | 24 | - name: Generate testvectors 25 | run: cd testvectorgen && go test ./... 26 | 27 | - name: Install node_modules 28 | if: steps.modules-cache.outputs.cache-hit != 'true' 29 | run: npm install 30 | - name: Test circom circuits 31 | run: npm run test 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .idea 4 | .vscode 5 | .DS_Store 6 | powersOfTau28_hez_final_17.ptau 7 | testvectorgen/**/testdata -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Build circom container that will use into github/workflows 2 | 3 | # Build circom 4 | FROM rust:1.59.0 as circom-instaler 5 | 6 | WORKDIR / 7 | 8 | RUN git clone https://github.com/iden3/circom.git && \ 9 | cd circom && \ 10 | cargo build --release && \ 11 | cargo install --path circom 12 | 13 | # Print version of rust 14 | RUN strip -g /usr/local/cargo/bin/circom \ 15 | && echo "CARGO_VERSION='$(cargo --version)'" >> /etc/image-info \ 16 | && echo "RUST_VERSION='$(rustc --version)'" >> /etc/image-info 17 | 18 | # Install node for run tests 19 | FROM node:16.14.2 20 | 21 | COPY --from=circom-instaler /usr/local/cargo/bin/circom /bin/ 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # circuits [![Tests](https://github.com/iden3/circuits/workflows/Tests/badge.svg)](https://github.com/iden3/circuits/actions?query=workflow%3ATests) 2 | 3 | Circuits used by the iden3 core protocol. 4 | 5 | The circuits of this repository are compatible with the [go-iden3-core implementation](https://github.com/iden3/go-iden3-core) 6 | 7 | # Building and trusted setup 8 | 9 | First install the npm dependencies: 10 | 11 | ```bash 12 | npm ci 13 | ``` 14 | 15 | Then build the circuit and do the "trusted" setup: 16 | 17 | ```bash 18 | ./compile-circuit.sh CIRCUIT_PATH PTAU_FILE_PATH 19 | ``` 20 | 21 | Examples: 22 | 23 | ```bash 24 | ./compile-circuit.sh circuits/auth.circom build/powersOfTau28_hez_final_16.ptau 25 | ./compile-circuit.sh circuits/stateTransition.circom build/powersOfTau28_hez_final_16.ptau 26 | ``` 27 | 28 | ## Work with `s3_util.js` script 29 | 30 | **Note**: Run `npm i` and ensure that environment _ACCESS_KEY_ID_ and _SECRET_ACCESS_KEY_ variables are defined. Script works with _./build_ folder which is located in project. 31 | 32 | ```bash 33 | 34 | export ACCESS_KEY_ID=... 35 | export SECRET_ACCESS_KEY=... 36 | 37 | ``` 38 | 39 | `s3_util.js` was written for: 40 | 41 | - Uploading circuits which are located in`./build` folder to S3 bucket in zip file. Next example uploads to S3 bucket (default bucket is `iden3-circuits-bucket`) with name `v1.zip`. 42 | 43 | ```bash 44 | node s3_util.js add v1 45 | ``` 46 | 47 | - Compressing circuits from `./build` folder to zip and save it to root project folder with name `v1.zip`. Example: 48 | 49 | ```bash 50 | node s3_util.js zip v1 51 | ``` 52 | 53 | - Removing zip file from S3 bucket (default bucket `iden3-circuits-bucket`) Example: 54 | 55 | ```bash 56 | node s3_util.js rm v1 57 | ``` 58 | 59 | ## Push docker container to github packages 60 | 1. Create [PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token); 61 | 2. Login to github registry: 62 | ```bash 63 | echo | docker login ghcr.io -u --password-stdin 64 | ``` 65 | 3. Build docker image with tag: 66 | ```bash 67 | docker build -t ghcr.io/iden3/circom: . 68 | ``` 69 | 4. Push docker image: 70 | ```bash 71 | docker push ghcr.io/iden3/circom: 72 | ``` 73 | 5. Update `.github/workflows/main.yaml` to the new image: 74 | ```yaml 75 | ... 76 | container: 77 | image: ghcr.io/iden3/circom: 78 | ... 79 | ``` 80 | -------------------------------------------------------------------------------- /circuits/auth/authV3.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../node_modules/circomlib/circuits/mux1.circom"; 4 | include "../../node_modules/circomlib/circuits/comparators.circom"; 5 | include "../../node_modules/circomlib/circuits/eddsaposeidon.circom"; 6 | include "../lib/idOwnership.circom"; 7 | include "../lib/utils/idUtils.circom"; 8 | include "../lib/utils/safeOne.circom"; 9 | 10 | template AuthV3(IdOwnershipLevels, onChainLevels) { 11 | signal input genesisID; 12 | // random number, which should be stored by user if there is a need to 13 | // generate the same userID (ProfileID) output for different proofs 14 | signal input profileNonce; 15 | 16 | // user state 17 | signal input state; 18 | signal input claimsTreeRoot; 19 | signal input revTreeRoot; 20 | signal input rootsTreeRoot; 21 | 22 | // Auth claim 23 | signal input authClaim[8]; 24 | 25 | // auth claim. merkle tree proof of inclusion to claim tree 26 | signal input authClaimIncMtp[IdOwnershipLevels]; 27 | 28 | // auth claim - rev nonce. merkle tree proof of non-inclusion to rev tree 29 | signal input authClaimNonRevMtp[IdOwnershipLevels]; 30 | signal input authClaimNonRevMtpNoAux; 31 | signal input authClaimNonRevMtpAuxHi; 32 | signal input authClaimNonRevMtpAuxHv; 33 | 34 | // challenge and it's signature 35 | signal input challenge; 36 | signal input challengeSignatureR8x; 37 | signal input challengeSignatureR8y; 38 | signal input challengeSignatureS; 39 | 40 | // global identity state tree on chain 41 | signal input gistRoot; 42 | // proof of inclusion or exclusion of the user in the global state 43 | signal input gistMtp[onChainLevels]; 44 | signal input gistMtpAuxHi; 45 | signal input gistMtpAuxHv; 46 | signal input gistMtpNoAux; 47 | 48 | // userID output signal will be assigned with user profile hash(UserID, nonce), 49 | // unless nonce == 0, in which case userID will be assigned with userGenesisID 50 | signal output userID; 51 | 52 | // get safe zero and one values to be used in ForceEqualIfEnabled 53 | signal {binary} one <== SafeOne()(genesisID); 54 | 55 | checkAuthV3(IdOwnershipLevels, onChainLevels)( 56 | one, 57 | genesisID, 58 | state, 59 | claimsTreeRoot, 60 | revTreeRoot, 61 | rootsTreeRoot, 62 | authClaim, 63 | authClaimIncMtp, 64 | authClaimNonRevMtp, 65 | authClaimNonRevMtpNoAux, 66 | authClaimNonRevMtpAuxHi, 67 | authClaimNonRevMtpAuxHv, 68 | challenge, 69 | challengeSignatureR8x, 70 | challengeSignatureR8y, 71 | challengeSignatureS, 72 | gistRoot, 73 | gistMtp, 74 | gistMtpAuxHi, 75 | gistMtpAuxHv, 76 | gistMtpNoAux 77 | ); 78 | 79 | /* ProfileID calculation */ 80 | userID <== SelectProfile()(genesisID, profileNonce); 81 | } 82 | 83 | template checkAuthV3(IdOwnershipLevels, onChainLevels) { 84 | signal input {binary} enabled; 85 | 86 | signal input genesisID; 87 | 88 | // user state 89 | signal input state; 90 | signal input claimsTreeRoot; 91 | signal input revTreeRoot; 92 | signal input rootsTreeRoot; 93 | 94 | // Auth claim 95 | signal input authClaim[8]; 96 | 97 | // auth claim. merkle tree proof of inclusion to claim tree 98 | signal input authClaimIncMtp[IdOwnershipLevels]; 99 | 100 | // auth claim - rev nonce. merkle tree proof of non-inclusion to rev tree 101 | signal input authClaimNonRevMtp[IdOwnershipLevels]; 102 | signal input authClaimNonRevMtpNoAux; 103 | signal input authClaimNonRevMtpAuxHi; 104 | signal input authClaimNonRevMtpAuxHv; 105 | 106 | // challenge signature 107 | signal input challenge; 108 | signal input challengeSignatureR8x; 109 | signal input challengeSignatureR8y; 110 | signal input challengeSignatureS; 111 | 112 | // global identity state tree on chain 113 | signal input gistRoot; 114 | // proof of inclusion or exclusion of the user in the global state 115 | signal input gistMtp[onChainLevels]; 116 | signal input gistMtpAuxHi; 117 | signal input gistMtpAuxHv; 118 | signal input gistMtpNoAux; 119 | 120 | /* id ownership check */ 121 | IdOwnership(IdOwnershipLevels)( 122 | enabled, 123 | state, 124 | claimsTreeRoot, 125 | authClaimIncMtp, 126 | authClaim, 127 | revTreeRoot, 128 | authClaimNonRevMtp, 129 | authClaimNonRevMtpNoAux, 130 | authClaimNonRevMtpAuxHi, 131 | authClaimNonRevMtpAuxHv, 132 | rootsTreeRoot, 133 | challenge, 134 | challengeSignatureR8x, 135 | challengeSignatureR8y, 136 | challengeSignatureS 137 | ); 138 | 139 | /* Check if state is genesis and if genesisId is valid */ 140 | 141 | signal cutState <== cutState()(state); 142 | 143 | component genesisIdParts = SplitID(); 144 | genesisIdParts.id <== genesisID; 145 | 146 | signal calculatedChecksum <== CalculateIdChecksum()(genesisIdParts.typ, genesisIdParts.genesis); 147 | ForceEqualIfEnabled()( 148 | enabled, 149 | [genesisIdParts.checksum, calculatedChecksum] 150 | ); 151 | 152 | signal isStateGenesis <== IsEqual()([genesisIdParts.genesis, cutState]); 153 | 154 | /* Check on-chain SMT inclusion existence */ 155 | 156 | signal genesisIDHash <== Poseidon(1)([genesisID]); 157 | 158 | SMTVerifier(onChainLevels)( 159 | enabled <== enabled, 160 | fnc <== isStateGenesis, // non-inclusion in case of genesis state, otherwise inclusion 161 | root <== gistRoot, 162 | siblings <== gistMtp, 163 | oldKey <== gistMtpAuxHi, 164 | oldValue <== gistMtpAuxHv, 165 | isOld0 <== gistMtpNoAux, 166 | key <== genesisIDHash, 167 | value <== state 168 | ); 169 | } 170 | -------------------------------------------------------------------------------- /circuits/authV3.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "auth/authV3.circom"; 4 | 5 | /* 6 | * The identity authorization circuit. 7 | * User ownership of the identity verified by signed challenge. 8 | * Auth claim should be in the user state and not revoked. 9 | * User state should be genesis or added to the global state tree (available in the smart contract). 10 | * The state is verified out of circuits by a verifier. 11 | * public signals: 12 | - userID 13 | - challenge 14 | - gistRoot 15 | */ 16 | component main {public [challenge, gistRoot]} = AuthV3(40, 64); 17 | -------------------------------------------------------------------------------- /circuits/credentialAtomicQueryV3.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "offchain/credentialAtomicQueryV3OffChain.circom"; 4 | 5 | /* 6 | public output signals: 7 | userID - user profile id 8 | merklized - `1` if claim is merklized 9 | issuerState - equals to issuerAuthState for sig, and to issuerClaimIdenState for mtp 10 | nullifier - sybil resistant user identifier for session id 11 | linkID - linked proof identifier 12 | */ 13 | component main{public [requestID, 14 | issuerID, 15 | issuerClaimNonRevState, 16 | claimSchema, 17 | slotIndex, 18 | claimPathKey, 19 | operator, 20 | value, 21 | valueArraySize, 22 | timestamp, 23 | isRevocationChecked, 24 | proofType, 25 | verifierID, 26 | nullifierSessionID 27 | ]} = credentialAtomicQueryV3OffChain(40, 32, 64); 28 | -------------------------------------------------------------------------------- /circuits/credentialAtomicQueryV3OnChain.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "./onchain/credentialAtomicQueryV3OnChain.circom"; 4 | 5 | /* 6 | public output signals: 7 | userID - user profile id 8 | merklized - `1` if claim is merklized 9 | issuerState - equals to issuerAuthState for sig, and to issuerClaimIdenState for mtp 10 | nullifier - sybil resistant user identifier for session id 11 | linkID - linked proof identifier 12 | */ 13 | component main{public [requestID, 14 | issuerID, 15 | issuerClaimNonRevState, 16 | timestamp, 17 | challenge, 18 | gistRoot, 19 | proofType, 20 | isBJJAuthEnabled 21 | ]} = credentialAtomicQueryV3OnChain(40, 32, 64, 40, 64); 22 | -------------------------------------------------------------------------------- /circuits/lib/idOwnership.circom: -------------------------------------------------------------------------------- 1 | /* 2 | # idOwnershipBySignature.circom 3 | 4 | Circuit to check that the prover is the owner of the identity 5 | - prover is owner of the private key 6 | - prover public key is in a ClaimKeyBBJJ that is inside its Identity State (in Claim tree) 7 | */ 8 | 9 | pragma circom 2.1.1; 10 | 11 | include "utils/claimUtils.circom"; 12 | include "utils/treeUtils.circom"; 13 | 14 | template IdOwnership(nLevels) { 15 | signal input {binary} enabled; 16 | 17 | signal input userState; 18 | 19 | signal input userClaimsTreeRoot; 20 | signal input userAuthClaimMtp[nLevels]; 21 | signal input userAuthClaim[8]; 22 | 23 | signal input userRevTreeRoot; 24 | signal input userAuthClaimNonRevMtp[nLevels]; 25 | signal input userAuthClaimNonRevMtpNoAux; 26 | signal input userAuthClaimNonRevMtpAuxHi; 27 | signal input userAuthClaimNonRevMtpAuxHv; 28 | 29 | signal input userRootsTreeRoot; 30 | 31 | signal input challenge; 32 | signal input challengeSignatureR8x; 33 | signal input challengeSignatureR8y; 34 | signal input challengeSignatureS; 35 | 36 | VerifyAuthClaimAndSignature(nLevels)( 37 | enabled, 38 | userClaimsTreeRoot, 39 | userAuthClaimMtp, 40 | userAuthClaim, 41 | userRevTreeRoot, 42 | userAuthClaimNonRevMtp, 43 | userAuthClaimNonRevMtpNoAux, 44 | userAuthClaimNonRevMtpAuxHi, 45 | userAuthClaimNonRevMtpAuxHv, 46 | challenge, 47 | challengeSignatureR8x, 48 | challengeSignatureR8y, 49 | challengeSignatureS 50 | ); 51 | 52 | checkIdenStateMatchesRoots()(enabled, userClaimsTreeRoot, userRevTreeRoot, userRootsTreeRoot, userState); 53 | } 54 | -------------------------------------------------------------------------------- /circuits/lib/linked/linkId.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../../node_modules/circomlib/circuits/comparators.circom"; 4 | include "../../../node_modules/circomlib/circuits/mux1.circom"; 5 | include "../../../node_modules/circomlib/circuits/poseidon.circom"; 6 | 7 | template LinkID() { 8 | signal input claimHash; 9 | signal input linkNonce; 10 | 11 | signal output out; 12 | 13 | signal isNonceZero <== IsZero()(linkNonce); 14 | 15 | signal linkID <== Poseidon(2)([claimHash, linkNonce]); 16 | 17 | out <== Mux1()( 18 | [linkID, 0], 19 | isNonceZero 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /circuits/lib/query/comparators.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../node_modules/circomlib/circuits/comparators.circom"; 4 | include "../../../node_modules/circomlib/circuits/gates.circom"; 5 | 6 | // Checks if value `in` is included is in value array 7 | // Returns 1 if at least one value is equal to `in`, 0 otherwise 8 | // valueArraySize - size of value array 9 | // Example: IN(3)(1, [12, 1231, 9999]) ==> 0 (1 is not in array of values) 10 | // Example: IN(3)(1231, [12, 1231, 9999]) ==> 1 (1231 is in array of values) 11 | template IN(valueArraySize){ 12 | 13 | signal input in; 14 | signal input value[valueArraySize]; 15 | signal output out; 16 | 17 | component eq[valueArraySize]; 18 | signal isEq[valueArraySize+1]; 19 | isEq[0] <== 0; 20 | for (var i=0; i 0 (0 is not in the first 3 elements of value array) 32 | template InWithDynamicArraySize(maxValueArraySize){ 33 | signal input in; 34 | signal input value[maxValueArraySize]; 35 | signal input valueArraySize; 36 | signal output out; 37 | 38 | assert(maxValueArraySize <= 256); 39 | 40 | component eq[maxValueArraySize]; 41 | signal isEq[maxValueArraySize+1]; 42 | signal lt[maxValueArraySize]; 43 | isEq[0] <== 0; 44 | for (var i=0; i out; 107 | } 108 | -------------------------------------------------------------------------------- /circuits/lib/query/modifiers.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../node_modules/circomlib/circuits/bitify.circom"; 4 | include "../../../node_modules/circomlib/circuits/mux1.circom"; 5 | include "../../../node_modules/circomlib/circuits/mux4.circom"; 6 | 7 | /* 8 | Modifier/computation operators: 9 | 16 - selective disclosure (16 = 10000 binary) 10 | */ 11 | 12 | // modifierValidatorOutputSelector validates modifier operation and selects output value 13 | template modifierValidatorOutputSelector() { 14 | signal input operator; 15 | signal input modifierOutputs[16]; 16 | signal output out; 17 | 18 | // parse operator to bits 19 | signal opBits[5] <== Num2Bits(5)(operator); // values 0-15 are query operators, 16-31 - modifiers/computations 20 | 21 | // modifier operation validation mux 22 | // it only validates that operator number is valid 23 | component modifierOpValid = Mux4(); 24 | modifierOpValid.s <== [opBits[0], opBits[1], opBits[2], opBits[3]]; 25 | modifierOpValid.c <== [ 26 | 1, // valid operator: 16 - selective disclosure (16-16 = index 0) 27 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 28 | ]; 29 | 30 | ForceEqualIfEnabled()( 31 | opBits[4], // equals to 1 for values 16-31 32 | [modifierOpValid.out, 1] 33 | ); 34 | 35 | // output value calculation 36 | signal modifierOutput <== Mux4()( 37 | s <== [opBits[0], opBits[1], opBits[2], opBits[3]], 38 | c <== modifierOutputs 39 | ); 40 | 41 | // output value only if modifier operation was selected 42 | out <== Mux1()( 43 | c <== [0, modifierOutput], // output 0 for non-modifier operations 44 | s <== opBits[4] // operator values 0-15 are query operators, 16-31 - modifiers/computations 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /circuits/lib/query/processQueryWithModifiers.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../node_modules/circomlib/circuits/comparators.circom"; 4 | include "query.circom"; 5 | include "modifiers.circom"; 6 | include "../utils/claimUtils.circom"; 7 | include "../utils/arraySizeValidator.circom"; 8 | 9 | template ProcessQueryWithModifiers(claimLevels, maxValueArraySize){ 10 | signal input enabled; 11 | signal input claimPathMtp[claimLevels]; 12 | signal input claimPathMtpNoAux; // 1 if aux node is empty, 0 if non-empty or for inclusion proofs 13 | signal input claimPathMtpAuxHi; // 0 for inclusion proof 14 | signal input claimPathMtpAuxHv; // 0 for inclusion proof 15 | signal input claimPathKey; // hash of path in merklized json-ld document 16 | signal input claimPathValue; // value in this path in merklized json-ld document 17 | signal input slotIndex; // slot index with value to check for non-merklized credentials 18 | signal input operator; 19 | signal input value[maxValueArraySize]; 20 | signal input valueArraySize; // actual size of value array - we don't want zero filled arrays to cause false positives for 0 as input to IN/NIN operators 21 | 22 | signal input issuerClaim[8]; 23 | signal input merklized; 24 | signal input merklizedRoot; 25 | 26 | // Modifier/Computation Operator output ($sd) 27 | signal output operatorOutput; 28 | 29 | signal isOpNoop <== IsZero()(operator); 30 | signal merklizedAndEnabled <== AND()(enabled, merklized); 31 | 32 | signal isOpExists <== IsEqual()([operator, 11]); 33 | 34 | // if operator == exists and value[0] == 0 ($exists == false), then claimPathNotExists = 1 (check non-inclusion), 35 | // otherwise claimPathNotExists = 0 (check inclusion) 36 | signal claimPathNotExists <== AND()(isOpExists, IsZero()(value[0])); 37 | 38 | // check path/in node exists in merkle tree specified by jsonldRoot 39 | SMTVerifier(claimLevels)( 40 | enabled <== AND()(merklizedAndEnabled, NOT()(isOpNoop)), // if merklize flag is 0 or enabled is 0 or it's NOOP operation --> skip MTP verification 41 | fnc <== claimPathNotExists, // inclusion (or non-inclusion in case exists==false) 42 | root <== merklizedRoot, 43 | siblings <== claimPathMtp, 44 | oldKey <== claimPathMtpAuxHi, 45 | oldValue <== claimPathMtpAuxHv, 46 | isOld0 <== claimPathMtpNoAux, 47 | key <== claimPathKey, 48 | value <== claimPathValue 49 | ); // 9585 constraints 50 | 51 | // select value from claim by slot index (0-7) 52 | signal slotValue <== getValueByIndex()(issuerClaim, slotIndex); 53 | 54 | // select value for query verification, 55 | // if claim is merklized merklizeFlag = `1|2`, take claimPathValue 56 | // if not merklized merklizeFlag = `0`, take value from selected slot 57 | signal fieldValue <== Mux1()( 58 | [slotValue, claimPathValue], 59 | merklized 60 | ); 61 | 62 | // For non-merklized credentials exists / non-exist operators should always fail 63 | ForceEqualIfEnabled()( 64 | AND()(enabled, NOT()(merklized)), 65 | [isOpExists, 0] 66 | ); 67 | 68 | // Restrict exists operator input values to 0 and 1 69 | ForceEqualIfEnabled()( 70 | AND()(enabled, isOpExists), 71 | [value[0] * (value[0] - 1), 0] 72 | ); 73 | 74 | 75 | ///////////////////////////////////////////////////////////////// 76 | // Query Operator Processing 77 | ///////////////////////////////////////////////////////////////// 78 | 79 | // verify value array length 80 | // 802 constraints (ArraySizeValidator+ForceEqualIfEnabled) 81 | signal arrSizeSatisfied <== ArraySizeValidator(maxValueArraySize)( 82 | valueArraySize <== valueArraySize, 83 | operator <== operator 84 | ); 85 | 86 | ForceEqualIfEnabled()( 87 | enabled, 88 | [arrSizeSatisfied, 1] 89 | ); 90 | 91 | // verify query 92 | // 1756 constraints (Query+LessThan+ForceEqualIfEnabled) 93 | signal querySatisfied <== Query(maxValueArraySize)( 94 | in <== fieldValue, 95 | value <== value, 96 | valueArraySize <== valueArraySize, 97 | operator <== operator 98 | ); 99 | 100 | signal isQueryOp <== LessThan(5)([operator, 16]); 101 | signal querySatisfiedEnabled <== AND()(enabled, isQueryOp); 102 | ForceEqualIfEnabled()( 103 | querySatisfiedEnabled, 104 | [querySatisfied, 1] 105 | ); 106 | 107 | ///////////////////////////////////////////////////////////////// 108 | // Modifier/Computation Operators Processing 109 | ///////////////////////////////////////////////////////////////// 110 | 111 | // selective disclosure 112 | // no need to calc anything, fieldValue is just passed as an output 113 | 114 | ///////////////////////////////////////////////////////////////// 115 | // Modifier Operator Validation & Output Preparation 116 | ///////////////////////////////////////////////////////////////// 117 | 118 | // output value only if modifier operation was selected 119 | operatorOutput <== modifierValidatorOutputSelector()( 120 | operator <== operator, 121 | modifierOutputs <== [ 122 | fieldValue, // 16 - selective disclosure (16-16 = index 0) 123 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 17-31 - not used 124 | ] 125 | ); 126 | } -------------------------------------------------------------------------------- /circuits/lib/query/query.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | include "../../../node_modules/circomlib/circuits/mux1.circom"; 3 | include "../../../node_modules/circomlib/circuits/mux4.circom"; 4 | include "../../../node_modules/circomlib/circuits/bitify.circom"; 5 | include "../../../node_modules/circomlib/circuits/comparators.circom"; 6 | include "../../../node_modules/circomlib/circuits/gates.circom"; 7 | include "comparators.circom"; 8 | 9 | /* 10 | Operators: 11 | Query operators: 12 | 0 - noop. Ignores all `in` and `value` passed to query, out 1 13 | 1 - equals 14 | 2 - less than 15 | 3 - greater than 16 | 4 - in 17 | 5 - not in 18 | 6 - not equals 19 | 7 - less than or equal 20 | 8 - greater than or equal 21 | 9 - between (value[0] <= in <= value[1]) 22 | 10 - not between 23 | 11 - exists (true / false) 24 | Modifier/computation operators: 25 | 16 - selective disclosure (16 = 10000 binary) 26 | */ 27 | 28 | // Query template works only with Query operators (0-15). 29 | // Returns 1 if query operator is satisfied, 0 otherwise. 30 | // For modifier/computation operators (16-31) it always returns 0. 31 | template Query (maxValueArraySize) { 32 | // signals 33 | signal input in; 34 | signal input value[maxValueArraySize]; 35 | signal input valueArraySize; 36 | signal input operator; 37 | signal output out; 38 | 39 | // Equals 40 | signal eq <== IsEqual()([in, value[0]]); 41 | 42 | // LessThan 43 | signal lt <== LessThan254()([in, value[0]]); // 767 constraints 44 | 45 | // lte 46 | signal lte <== OR()(lt, eq); // lte === lt || eq 47 | 48 | // GreaterThan 49 | signal gt <== NOT()(lte); // gt === !lte 50 | 51 | // gte 52 | signal gte <== NOT()(lt); // gte === !lt 53 | 54 | // in w/o - 65668, IN - 65860( 192), InWithDynamicArraySize - 66372 (704) 55 | signal inComp <== InWithDynamicArraySize(maxValueArraySize)(in, value, valueArraySize); 56 | 57 | // between (value[0] <= in <= value[1]) 58 | signal gt2 <== GreaterThan254()([in, value[1]]); 59 | signal lte2 <== NOT()(gt2); // lte === !gt 60 | signal between <== AND()(gte, lte2); 61 | 62 | signal opBits[5] <== Num2Bits(5)(operator); // values 0-15 are query operators, 16-31 - modifiers/computations 63 | 64 | // query operator mux 65 | component queryOpSatisfied = Mux4(); 66 | queryOpSatisfied.s <== [opBits[0], opBits[1], opBits[2], opBits[3]]; 67 | // We don't use 5th bit (opBits[4]) here; which specifies whether operator is query or 68 | // modifier/computation operator. It's used in the final mux. 69 | _ <== opBits[4]; 70 | 71 | queryOpSatisfied.c[0] <== 1; // noop; always succeeds 72 | queryOpSatisfied.c[1] <== eq; 73 | queryOpSatisfied.c[2] <== lt; 74 | queryOpSatisfied.c[3] <== gt; 75 | queryOpSatisfied.c[4] <== inComp; // in 76 | queryOpSatisfied.c[5] <== NOT()(inComp); // nin 77 | queryOpSatisfied.c[6] <== NOT()(eq); // neq 78 | queryOpSatisfied.c[7] <== lte; // lte === !gt 79 | queryOpSatisfied.c[8] <== gte; // gte === !lt 80 | queryOpSatisfied.c[9] <== between; // between 81 | queryOpSatisfied.c[10] <== NOT()(between); // not between 82 | queryOpSatisfied.c[11] <== 1; // exists(true/false) - actual check is done by checking inclusion/non-inclusion of claimPathKey in merklized root by SMTVerifier outside 83 | queryOpSatisfied.c[12] <== 0; // not used 84 | queryOpSatisfied.c[13] <== 0; // not used 85 | queryOpSatisfied.c[14] <== 0; // not used 86 | queryOpSatisfied.c[15] <== 0; // not used 87 | 88 | // final output mux 89 | out <== Mux1()( 90 | s <== opBits[4], // specifies whether operator is query or modifier/computation operator 91 | c <== [queryOpSatisfied.out, 0] 92 | ); 93 | } 94 | 95 | 96 | -------------------------------------------------------------------------------- /circuits/lib/stateTransition.circom: -------------------------------------------------------------------------------- 1 | 2 | pragma circom 2.1.1; 3 | 4 | include "../../node_modules/circomlib/circuits/babyjub.circom"; 5 | include "../../node_modules/circomlib/circuits/comparators.circom"; 6 | include "../../node_modules/circomlib/circuits/poseidon.circom"; 7 | include "../../node_modules/circomlib/circuits/bitify.circom"; 8 | include "../../node_modules/circomlib/circuits/smt/smtverifier.circom"; 9 | include "../../node_modules/circomlib/circuits/smt/smtprocessor.circom"; 10 | include "utils/safeOne.circom"; 11 | include "idOwnership.circom"; 12 | 13 | template StateTransition(IdOwnershipLevels) { 14 | signal input userID; 15 | signal input oldUserState; 16 | signal input newUserState; 17 | signal input isOldStateGenesis; 18 | 19 | signal input claimsTreeRoot; 20 | signal input authClaimMtp[IdOwnershipLevels]; 21 | signal input authClaim[8]; 22 | 23 | signal input revTreeRoot; 24 | signal input authClaimNonRevMtp[IdOwnershipLevels]; 25 | signal input authClaimNonRevMtpNoAux; 26 | signal input authClaimNonRevMtpAuxHv; 27 | signal input authClaimNonRevMtpAuxHi; 28 | 29 | signal input rootsTreeRoot; 30 | 31 | signal input signatureR8x; 32 | signal input signatureR8y; 33 | signal input signatureS; 34 | 35 | signal input newClaimsTreeRoot; 36 | signal input newAuthClaimMtp[IdOwnershipLevels]; 37 | signal input newRevTreeRoot; 38 | signal input newRootsTreeRoot; 39 | 40 | // get safe one values to be used in ForceEqualIfEnabled 41 | signal {binary} one <== SafeOne()(userID); 42 | 43 | signal cutId <== cutId()(userID); 44 | 45 | signal cutState <== cutState()(oldUserState); 46 | 47 | signal isCutIdEqualToCutState <== IsEqual()([cutId, cutState]); 48 | 49 | // if isOldStateGenesis != 0 then old state is genesis 50 | // and we must check that userID was derived from that state 51 | (1 - isCutIdEqualToCutState) * isOldStateGenesis === 0; 52 | 53 | // check newUserState is not zero 54 | signal stateIsNotZero <== IsZero()(newUserState); 55 | ForceEqualIfEnabled()(one, [stateIsNotZero, 0]); 56 | 57 | // old & new state checks 58 | signal oldNewEqual <== IsEqual()([oldUserState, newUserState]); 59 | ForceEqualIfEnabled()(one, [oldNewEqual, 0]); 60 | 61 | // check userID ownership by correct signature of a hash of old state and new state 62 | signal challenge <== Poseidon(2)([oldUserState, newUserState]); 63 | 64 | IdOwnership(IdOwnershipLevels)( 65 | one, 66 | oldUserState, 67 | claimsTreeRoot, 68 | authClaimMtp, 69 | authClaim, 70 | revTreeRoot, 71 | authClaimNonRevMtp, 72 | authClaimNonRevMtpNoAux, 73 | authClaimNonRevMtpAuxHi, 74 | authClaimNonRevMtpAuxHv, 75 | rootsTreeRoot, 76 | challenge, 77 | signatureR8x, 78 | signatureR8y, 79 | signatureS 80 | ); 81 | 82 | signal authClaimHi, authClaimHv; 83 | (authClaimHi, authClaimHv) <== getClaimHiHv()(authClaim); 84 | 85 | // check auth claim exists in newClaimsTreeRoot and newUserState 86 | checkClaimExists(IdOwnershipLevels)(one, authClaimHi, authClaimHv, newAuthClaimMtp, newClaimsTreeRoot); 87 | 88 | checkIdenStateMatchesRoots()(one, newClaimsTreeRoot, newRevTreeRoot, newRootsTreeRoot, newUserState); 89 | } 90 | -------------------------------------------------------------------------------- /circuits/lib/utils/arraySizeValidator.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | include "../../../node_modules/circomlib/circuits/mux1.circom"; 3 | include "../../../node_modules/circomlib/circuits/mux4.circom"; 4 | include "../../../node_modules/circomlib/circuits/bitify.circom"; 5 | include "../../../node_modules/circomlib/circuits/comparators.circom"; 6 | include "../../../node_modules/circomlib/circuits/gates.circom"; 7 | include "../query/comparators.circom"; 8 | /* 9 | Operators: 10 | Query operators - valueArraySize 11 | 0 - noop - 0 elements 12 | 1 - equals - 1 element 13 | 2 - less than - 1 element 14 | 3 - greater than - 1 element 15 | 4 - in - less or eq than maxValueArraySize 16 | 5 - not in - less or eq than maxValueArraySize 17 | 6 - not equals - 1 element 18 | 7 - less than or equal - 1 element 19 | 8 - greater than or equal - 1 element 20 | 9 - between - 2 elements 21 | 10 - not between - 2 elements 22 | 11 - exists - 1 elements (true/false) 23 | Modifier/computation operators: 24 | 16 - selective disclosure (16 = 10000 binary) - 0 elements 25 | 17-31 - 0 elements 26 | */ 27 | 28 | // ArraySizeValidator template check valueArraySize for query operators 29 | template ArraySizeValidator (maxValueArraySize) { 30 | // signals 31 | signal input valueArraySize; 32 | signal input operator; 33 | signal output out; 34 | 35 | signal sizeEqZero <== IsEqual()([valueArraySize, 0]); 36 | signal sizeEqOne <== IsEqual()([valueArraySize, 1]); 37 | signal sizeEqTwo <== IsEqual()([valueArraySize, 2]); 38 | signal sizeLessOrEqMax <== LessThan(8)([valueArraySize, maxValueArraySize + 1]); 39 | 40 | signal sizeNotEqZero <== NOT()(sizeEqZero); 41 | signal moreThanZeroLessOrEqMax <== AND()(sizeNotEqZero, sizeLessOrEqMax); 42 | 43 | signal opBits[5] <== Num2Bits(5)(operator); // values 0-15 are query operators, 16-31 - modifiers/computations 44 | 45 | assert(maxValueArraySize <= 256); 46 | 47 | // query operator mux 48 | component mux = Mux4(); 49 | mux.s <== [opBits[0], opBits[1], opBits[2], opBits[3]]; 50 | 51 | // We don't use 5th bit (opBits[4]) here; which specifies whether operator is query or 52 | // modifier/computation operator. It's used in the final mux. 53 | _ <== opBits[4]; 54 | 55 | mux.c[0] <== sizeEqZero; // noop; skip execution 56 | mux.c[1] <== sizeEqOne; // equals 57 | mux.c[2] <== sizeEqOne; // lt 58 | mux.c[3] <== sizeEqOne; // gt 59 | mux.c[4] <== moreThanZeroLessOrEqMax; // in 60 | mux.c[5] <== moreThanZeroLessOrEqMax; // nin 61 | mux.c[6] <== sizeEqOne; // neq 62 | mux.c[7] <== sizeEqOne; // lte 63 | mux.c[8] <== sizeEqOne; // gte 64 | mux.c[9] <== sizeEqTwo; // between 65 | mux.c[10] <== sizeEqTwo; // not between 66 | mux.c[11] <== sizeEqOne; // exist 67 | mux.c[12] <== sizeEqZero; // not used 68 | mux.c[13] <== sizeEqZero; // not used 69 | mux.c[14] <== sizeEqZero; // not used 70 | mux.c[15] <== sizeEqZero; // not used 71 | 72 | // final output mux 73 | out <== Mux1()( 74 | s <== opBits[4], // specifies whether operator is query or modifier/computation operator 75 | c <== [mux.out, sizeEqZero] 76 | ); 77 | 78 | } 79 | 80 | 81 | -------------------------------------------------------------------------------- /circuits/lib/utils/babyjubjub.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../../node_modules/circomlib/circuits/comparators.circom"; 4 | 5 | template ForceBabyCheckIfEnabled() { 6 | signal input {binary} enabled; 7 | signal input x; 8 | signal input y; 9 | 10 | signal x2; 11 | signal y2; 12 | 13 | var a = 168700; 14 | var d = 168696; 15 | 16 | x2 <== x*x; 17 | y2 <== y*y; 18 | 19 | ForceEqualIfEnabled()(enabled, [a*x2 + y2, 1 + d*x2*y2]); 20 | } -------------------------------------------------------------------------------- /circuits/lib/utils/idUtils.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../node_modules/circomlib/circuits/bitify.circom"; 4 | include "../../../node_modules/circomlib/circuits/poseidon.circom"; 5 | include "../../../node_modules/circomlib/circuits/mux1.circom"; 6 | include "./safeOne.circom"; 7 | 8 | template ProfileID(){ 9 | signal input in; 10 | signal input nonce; 11 | signal output out; 12 | 13 | signal hash <== Poseidon(2)([in, nonce]); 14 | signal genesis <== TakeNBits(27*8)(hash); 15 | 16 | component genesisIdParts = SplitID(); 17 | genesisIdParts.id <== in; 18 | 19 | out <== NewID()(genesisIdParts.typ, genesis); 20 | 21 | // explicitly state that these signals are not used and it's ok 22 | _ <== genesisIdParts.genesis; 23 | _ <== genesisIdParts.checksum; 24 | } 25 | 26 | // Split ID into type, genesys and checksum 27 | template SplitID() { 28 | signal input id; 29 | signal output typ; 30 | signal output genesis; 31 | signal output checksum; 32 | 33 | component bs = Num2Bits(248); 34 | bs.in <== id; 35 | 36 | // checksum bytes are swapped in ID. 31-th byte is first and 30-th is second. 37 | component checksumBits = Bits2Num(16); 38 | for (var i = 0; i < 16; i++) { 39 | checksumBits.in[i] <== bs.out[29 * 8 + i]; 40 | } 41 | checksum <== checksumBits.out; 42 | 43 | component genesisBits = Bits2Num(216); 44 | for (var i = 0; i < 216; i++) { 45 | genesisBits.in[i] <== bs.out[i + 16]; 46 | } 47 | genesis <== genesisBits.out; 48 | 49 | component typBits = Bits2Num(16); 50 | for (var i = 0; i < 16; i++) { 51 | typBits.in[i] <== bs.out[i]; 52 | } 53 | typ <== typBits.out; 54 | 55 | // explicitly state that some of these signals are not used and it's ok 56 | for (var i=0; i<248; i++) { 57 | _ <== bs.out[i]; 58 | } 59 | } 60 | 61 | template NewID() { 62 | signal input typ; 63 | signal input genesis; 64 | signal output out; 65 | 66 | signal checksum <== CalculateIdChecksum()(typ, genesis); 67 | 68 | out <== GatherID()(typ, genesis, checksum); 69 | } 70 | 71 | // return 31-byte ID made up from type, genesis and checksum 72 | template GatherID() { 73 | signal input typ; 74 | signal input genesis; 75 | signal input checksum; 76 | signal output out; 77 | 78 | component idBits = Bits2Num(31*8); 79 | 80 | component checksumBits = Num2Bits(2*8); 81 | checksumBits.in <== checksum; 82 | for (var i = 0; i < 16; i++) { 83 | idBits.in[29*8+i] <== checksumBits.out[i]; 84 | } 85 | 86 | component genesisBits = Num2Bits(27*8); 87 | genesisBits.in <== genesis; 88 | for (var i = 0; i < 27 * 8; i++) { 89 | idBits.in[2*8+i] <== genesisBits.out[i]; 90 | } 91 | 92 | component typBits = Num2Bits(2*8); 93 | typBits.in <== typ; 94 | for (var i = 0; i < 2 * 8; i++) { 95 | idBits.in[i] <== typBits.out[i]; 96 | } 97 | 98 | out <== idBits.out; 99 | } 100 | 101 | // Take least significant n bits 102 | template TakeNBits(n) { 103 | signal input in; 104 | signal output out; 105 | 106 | assert(n <= 254); 107 | 108 | // We take only n least significant bits from 254 bit number. 109 | component bits = Num2Bits_strict(); 110 | bits.in <== in; 111 | 112 | component outBits = Bits2Num(n); 113 | for (var i = 0; i < n; i++) { 114 | outBits.in[i] <== bits.out[i]; 115 | } 116 | out <== outBits.out; 117 | 118 | // explicitly state that these signals are not used and it's ok 119 | for (var i=n; i<254; i++) { 120 | _ <== bits.out[i]; 121 | } 122 | } 123 | 124 | template CalculateIdChecksum() { 125 | signal input typ; // 2 bytes 126 | signal input genesis; // 27 bytes 127 | signal output out; 128 | 129 | signal sum[30]; 130 | var k = 0; 131 | sum[0] <== 0; 132 | 133 | component typBits = Num2Bits(16); 134 | typBits.in <== typ; 135 | for (var i = 0; i < 16; i = i + 8) { 136 | var lc1 = 0; 137 | var e2 = 1; 138 | for (var j = 0; j < 8; j++) { 139 | lc1 += typBits.out[i + j] * e2; 140 | e2 = e2 + e2; 141 | } 142 | sum[k+1] <== sum[k] + lc1; 143 | k++; 144 | } 145 | 146 | component genesisBits = Num2Bits(27*8); 147 | genesisBits.in <== genesis; 148 | for (var i = 0; i < 27*8; i = i + 8) { 149 | var lc1 = 0; 150 | var e2 = 1; 151 | for (var j = 0; j < 8; j++) { 152 | lc1 += genesisBits.out[i + j] * e2; 153 | e2 = e2 + e2; 154 | } 155 | sum[k+1] <== sum[k] + lc1; 156 | k++; 157 | } 158 | 159 | // no need to cut last 16 bits of sum, because max value of sum is 255*29 = 7395 = 0x1CE3 - 16 bits 160 | out <== sum[k]; 161 | } 162 | 163 | // SelectProfile `out` output signal will be assigned with user profile, 164 | // unless nonce == 0, in which case profile will be assigned with `in` id 165 | template SelectProfile() { 166 | signal input in; 167 | signal input nonce; 168 | 169 | signal output out; 170 | 171 | signal isNonceZero <== IsZero()(nonce); 172 | signal calcProfile <== ProfileID()(in, nonce); 173 | 174 | out <== Mux1()( 175 | [calcProfile, in], 176 | isNonceZero 177 | ); 178 | } 179 | 180 | template cutId() { 181 | signal input in; 182 | signal output out; 183 | 184 | signal idBits[248] <== Num2Bits(248)(in); 185 | 186 | component cut = Bits2Num(216); 187 | for (var i=16; i<248-16; i++) { 188 | cut.in[i-16] <== idBits[i]; 189 | } 190 | out <== cut.out; 191 | } 192 | 193 | template cutState() { 194 | signal input in; 195 | signal output out; 196 | 197 | signal stateBits[254] <== Num2Bits_strict()(in); 198 | 199 | component cut = Bits2Num(216); 200 | // two most significant bits of 256-bit number are always 0, because we have 254-bit prime field 201 | for (var i=0; i<216-2; i++) { 202 | cut.in[i] <== stateBits[i+16+16+8]; 203 | } 204 | cut.in[214] <== 0; 205 | cut.in[215] <== 0; 206 | out <== cut.out; 207 | } 208 | 209 | // getIdenState calculates the Identity state out of the claims tree root, 210 | // revocations tree root and roots tree root. 211 | template getIdenState() { 212 | signal input claimsTreeRoot; 213 | signal input revTreeRoot; 214 | signal input rootsTreeRoot; 215 | 216 | signal output idenState <== Poseidon(3)([ 217 | claimsTreeRoot, 218 | revTreeRoot, 219 | rootsTreeRoot 220 | ]); 221 | } 222 | -------------------------------------------------------------------------------- /circuits/lib/utils/nullify.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../node_modules/circomlib/circuits/comparators.circom"; 4 | include "../../../node_modules/circomlib/circuits/gates.circom"; 5 | include "../../../node_modules/circomlib/circuits/mux1.circom"; 6 | include "../../../node_modules/circomlib/circuits/poseidon.circom"; 7 | 8 | template Nullify() { 9 | signal input genesisID; 10 | signal input claimSubjectProfileNonce; 11 | signal input claimSchema; 12 | signal input verifierID; 13 | signal input nullifierSessionID; 14 | 15 | signal output nullifier; 16 | 17 | signal isZeroNonce <== IsZero()(claimSubjectProfileNonce); 18 | signal isZeroVerifierID <== IsZero()(verifierID); 19 | signal isZeroNullifierSessionID <== IsZero()(nullifierSessionID); 20 | 21 | signal hash <== Poseidon(5)([genesisID, claimSubjectProfileNonce, claimSchema, verifierID, nullifierSessionID]); 22 | 23 | signal isZero1 <== OR()(isZeroNonce, isZeroVerifierID); 24 | signal isZero2 <== OR()(isZero1, isZeroNullifierSessionID); 25 | 26 | nullifier <== Mux1()( 27 | [hash, 0], 28 | isZero2 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /circuits/lib/utils/queryHash.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | include "./spongeHash.circom"; 3 | 4 | template QueryHash(maxValueArraySize) { 5 | signal input value[maxValueArraySize]; 6 | signal input claimSchema; 7 | signal input slotIndex; 8 | signal input operator; 9 | signal input claimPathKey; 10 | signal input valueArraySize; 11 | signal input merklized; 12 | signal input isRevocationChecked; 13 | signal input verifierID; 14 | signal input nullifierSessionID; 15 | 16 | signal output out; 17 | 18 | ///////////////////////////////////////////////////////////////// 19 | // Calculate query hash 20 | ///////////////////////////////////////////////////////////////// 21 | // 4950 constraints (SpongeHash+Poseidon) 22 | signal valueHash <== SpongeHash(maxValueArraySize, 6)(value); // 6 - max size of poseidon hash available on-chain 23 | signal firstPartQueryHash <== Poseidon(6)([ 24 | claimSchema, 25 | slotIndex, 26 | operator, 27 | claimPathKey, 28 | merklized, 29 | valueHash 30 | ]); 31 | 32 | out <== Poseidon(6)([ 33 | firstPartQueryHash, 34 | valueArraySize, 35 | isRevocationChecked, 36 | verifierID, 37 | nullifierSessionID, 38 | 0 39 | ]); 40 | } 41 | -------------------------------------------------------------------------------- /circuits/lib/utils/safeOne.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../node_modules/circomlib/circuits/comparators.circom"; 4 | include "../../../node_modules/circomlib/circuits/gates.circom"; 5 | 6 | ///////////////////////////////////////////////////////////////// 7 | // SafeZeroOne calculates safe one signals from any signal coming from outside of circuit 8 | // It is needed because linear constraints are not giving real security guarantees and circom is 9 | // removing them during optimization pass. 10 | // Because of this `===` without multiplications gives 0 constraints!!! 11 | // ForceEqualIfEnabled(1, [x, y]) gives 0 too. 12 | // Only ForceEqualIfEnabled(enabled, [x, y]) with `enabled` from input signal or signal safely derived 13 | // from input signal generates constraints. 14 | // That's why we need to calculate safe zero and one signals from input signal. 15 | ///////////////////////////////////////////////////////////////// 16 | template SafeOne() { 17 | signal input inputSignal; 18 | signal tmp <== IsZero()(inputSignal); 19 | signal tmp2 <== NOT()(tmp); 20 | signal zero <== IsEqual()([tmp, tmp2]); 21 | signal output {binary} one <== IsZero()(zero); 22 | zero * one === 0; 23 | } 24 | -------------------------------------------------------------------------------- /circuits/lib/utils/spongeHash.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../node_modules/circomlib/circuits/poseidon.circom"; 4 | 5 | // Because of the way the Poseidon hash function is implemented for Solidity, the max number of inputs must be 6 6 | template SpongeHash(arraySize, hashFnBatchSize) { 7 | signal input in[arraySize]; 8 | signal output out; 9 | var batchSize = hashFnBatchSize - 1; // 1 input is reserved for the hash of the previous iteration 10 | var iterationCount = 0; 11 | component firstPoseidon = Poseidon(hashFnBatchSize); 12 | for(var i = 0; i < hashFnBatchSize; i++) { 13 | firstPoseidon.inputs[i] <== getArrayValueByIndex(in, arraySize, i); 14 | } 15 | 16 | var restLength = arraySize - hashFnBatchSize > 0 ? arraySize - hashFnBatchSize : 0; 17 | if (restLength > 0) { 18 | var r = restLength % batchSize; 19 | var diff = r == 0 ? 0 : batchSize - r; 20 | iterationCount = (restLength + diff) / batchSize; 21 | } 22 | 23 | signal fullHash[iterationCount+1]; 24 | 25 | fullHash[0] <== firstPoseidon.out; 26 | 27 | component poseidon[iterationCount]; 28 | for(var i = 0; i < iterationCount; i++) { 29 | var elemIdx = i * batchSize + hashFnBatchSize ; 30 | poseidon[i] = Poseidon(hashFnBatchSize); 31 | 32 | poseidon[i].inputs[0] <== fullHash[i]; 33 | 34 | for (var j = 0; j < batchSize; j++) 35 | { 36 | poseidon[i].inputs[j+1] <== getArrayValueByIndex(in, arraySize, elemIdx + j); 37 | } 38 | 39 | fullHash[i+1] <== poseidon[i].out; 40 | } 41 | 42 | out <== fullHash[iterationCount]; 43 | } 44 | 45 | 46 | function getArrayValueByIndex(valueArray, arraySize, idx) { 47 | if(idx < arraySize) { 48 | return valueArray[idx]; 49 | } else{ 50 | return 0; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /circuits/lib/utils/tags-managing.circom: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | pragma circom 2.1.5; 20 | 21 | include "../../../node_modules/circomlib/circuits/bitify.circom"; 22 | include "../../../node_modules/circomlib/circuits/comparators.circom"; 23 | 24 | 25 | // The templates and functions in this file are general and work for any prime field 26 | 27 | // To consult the tags specifications check tags-specifications.circom 28 | 29 | 30 | /* 31 | *** AddBinaryTag(n): template that adds the constraints needed to ensure that a signal in is binary and adds the tag binary to the input 32 | - Inputs: in -> field value 33 | - Output: out -> same value as in, but including binary tag 34 | satisfies tag binary 35 | 36 | Example: AddBinaryTag()(1) = 1 37 | Note: in case the input in is not binary then the generated system of constraints does not have any solution for that input. 38 | For instance, AddBinaryTag()(10) -> no solution 39 | 40 | */ 41 | 42 | template AddBinaryTag () { 43 | signal input in; 44 | signal output {binary} out; 45 | 46 | in * (in - 1) === 0; 47 | out <== in; 48 | } 49 | 50 | /* 51 | *** AddBinaryArrayTag(n): template that adds the constraints needed to ensure that all signal of an array of n elements are binary and adds the tag binary to the input 52 | - Inputs: in[n] -> array of n field elements 53 | - Output: out[n] -> same value as in, but including binary tag 54 | satisfies tag binary 55 | 56 | Example: AddBinaryArrayTag(2)([0,1]) = [0,1] 57 | Note: in case the input contains a non binary element then the generated system of constraints does not have any solution for that input. 58 | For instance, AddBinaryArrayTag(2)([0,10]) -> no solution 59 | 60 | */ 61 | 62 | template AddBinaryArrayTag(n) { 63 | signal input in[n]; 64 | signal output {binary} out[n]; 65 | 66 | for (var i = 0; i < n; i++) { 67 | in[i] * (in[i] - 1) === 0; 68 | } 69 | out <== in; 70 | } 71 | 72 | /* 73 | *** ForceBinary(n): template that adds the tag binary to a signal without adding the constraints needed to ensure that the signal is binary 74 | - Inputs: in -> field value 75 | - Output: out -> same value as in, but including binary tag 76 | DOES NOT CHECK THAT out satisfies tag binary 77 | 78 | Example: ForceBinary()(5) = 5 79 | Note: the template does not check if the input actually satisfies the binary specification --> Use carefully, potentially dangerous template 80 | For instance, ForceBinary()(10) = 10, including tag binary 81 | 82 | */ 83 | 84 | template ForceBinary() { 85 | signal input in; 86 | signal output {binary} out; 87 | 88 | out <== in; 89 | } 90 | 91 | /* 92 | *** ForceBinaryArray(n): template that adds the tag binary to an array of n signals without adding the constraints needed to ensure that the signals are binary 93 | - Inputs: in[n] -> array of field values 94 | - Output: out[n] -> same value as in, but including binary tag 95 | DOES NOT CHECK THAT out satisfies tag binary 96 | 97 | Example: ForceBinaryArray()([1,0]) = [1, 0], including tag binary 98 | Note: the template does not check if the input actually satisfies the binary specification --> Use carefully, potentially dangerous template 99 | For instance, ForceBinaryArray()([0, 10]) = [0, 10], including tag binary 100 | 101 | */ 102 | 103 | template ForceBinaryArray(n) { 104 | signal input in[n]; 105 | signal output {binary} out[n]; 106 | 107 | out <== in; 108 | } 109 | 110 | /* 111 | *** AddMaxbitTag(n): template that adds the constraints needed to ensure that a signal can be expressed using n bits(that is value is in [0, 2**n)) and adds the tag maxbit = n to the input 112 | - Inputs: in -> field value 113 | - Output: out -> same value as in, but including maxbit tag with out.maxbit = n 114 | satisfies tag out.maxbit = n 115 | 116 | Example: AddMaxbitTag(5)(14) = 14 117 | Note: in case the input in does not satisfy the specification of maxbit then the generated system of constraints does not have any solution for that input. 118 | For instance, AddMaxbitTag(3)(100) -> no solution 119 | 120 | */ 121 | 122 | template AddMaxbitTag(n) { 123 | signal input in; 124 | signal output {maxbit} out; 125 | 126 | _ <== Num2Bits(n)(in); 127 | 128 | out.maxbit = n; 129 | out <== in; 130 | } 131 | 132 | /* 133 | *** AddMaxbitArrayTag(n): template that adds the constraints needed to ensure that the signals an array of length m can be expressed using n bits(that is, that is value is in [0, 2**n)) and adds the tag maxbit = n to the input 134 | - Inputs: in[m] -> array of m field values 135 | - Output: out[m] -> same values as in, but including maxbit tag with out.maxbit = n 136 | satisfies tag out.maxbit = n 137 | 138 | Example: AddMaxbitTag(5, 2)([3,14]) = [3, 14] with tag maxbit = 5 139 | Note: in case the a signal of the input in does not satisfy the specification of maxbit then the generated system of constraints does not have any solution for that input. 140 | For instance, AddMaxbitTag(3, 2)([3, 100]) -> no solution 141 | 142 | */ 143 | 144 | 145 | template AddMaxbitArrayTag(n,m) { 146 | signal input in[m]; 147 | signal output {maxbit} out[m]; 148 | 149 | out.maxbit = n; 150 | 151 | for (var i = 0; i < m; i++) { 152 | _ <== Num2Bits(n)(in[i]); 153 | } 154 | 155 | in ==> out; 156 | } 157 | 158 | /* 159 | *** ForceMaxbitTag(n): template that adds the tag maxbit = n to the input without adding any constraints to ensure that the input especification of the tag 160 | - Inputs: in -> field value 161 | - Output: out -> same value as in, but including maxbit tag with out.maxbit = n 162 | DOES NOT CHECK THAT out satisfies tag out.maxbit = n 163 | 164 | Note: the template does not check if the input actually satisfies the maxbit specification --> Use carefully, potentially dangerous template 165 | For instance, ForceMaxbitTag(2)(10) = 10, including tag maxbit = 2 166 | 167 | */ 168 | 169 | 170 | template ForceMaxbit(n) { 171 | signal input in; 172 | signal output {maxbit} out; 173 | 174 | out.maxbit = n; 175 | in ==> out; 176 | } 177 | 178 | /* 179 | *** ForceMaxbitArrayTag(n, m): template that adds the tag maxbit = n to the input that is an array of m signals without adding any constraints to ensure that the inputs especification of the tag 180 | - Inputs: in[m] -> field value 181 | - Output: out[m] -> same value as in, but including maxbit tag with out.maxbit = n 182 | DOES NOT CHECK THAT out satisfies tag out.maxbit = n 183 | 184 | Note: the template does not check if the input actually satisfies the maxbit specification --> Use carefully, potentially dangerous template 185 | For instance, ForceMaxbitTag(2,2)([0,10]) = [0,10], including tag maxbit = 2 186 | 187 | */ 188 | 189 | template ForceMaxbitArray(n,m) { 190 | signal input in[m]; 191 | signal output {maxbit} out[m]; 192 | 193 | out.maxbit = n; 194 | 195 | in ==> out; 196 | } 197 | 198 | 199 | /* 200 | *** AddMaxValueTag(n): template that adds the constraints needed to ensure that a signal is smaller or equal than a given value n and adds the tag max = n to the input 201 | - Inputs: in -> field value 202 | - Output: out -> same value as in, but including max tag with out.max = n 203 | satisfies tag out.max = n 204 | 205 | Example: AddMaxValueTag(15)(14) = 14 and can be satisfied 206 | Note: in case the input in does not satisfy the specification of max then the generated system of constraints does not have any solution for that input. 207 | For instance, AddMaxValueTag(3)(100) -> no solution 208 | 209 | */ 210 | 211 | template AddMaxValueTag(n) { 212 | signal input in; 213 | signal output {max} out; 214 | 215 | signal {maxbit} aux[2]; 216 | aux.maxbit = nbits(n); 217 | aux[0] <== AddMaxbitTag(nbits(n))(in); // to ensure the correct size 218 | aux[1] <== n; 219 | 220 | signal out1 <== LessEqThan(n)(aux); 221 | out1 === 1; 222 | out.max = n; 223 | out <== in; 224 | } 225 | 226 | /* 227 | *** AddMaxAbsValueTag(n): template that adds the constraints needed to ensure that the absolute value of a signal is smaller or equal than a given value n and adds the tag max_abs = n to the input 228 | - Inputs: in -> field value 229 | - Output: out -> same value as in, but including max_abs tag with out.max_abs = n 230 | satisfies tag out.max_abs = n 231 | 232 | Example: AddMaxValueTag(15)(-14) = 14 and can be satisfied 233 | Note: in case the input in does not satisfy the specification of max_abs then the generated system of constraints does not have any solution for that input. 234 | For instance, AddMaxAbsValueTag(33)(-100) -> no solution 235 | 236 | */ 237 | 238 | template AddMaxAbsValueTag(n){ 239 | signal input in; 240 | signal output {max_abs} out; 241 | 242 | var needed_bits = nbits(2 * n); 243 | 244 | signal {maxbit} aux[2]; 245 | aux.maxbit = needed_bits; 246 | aux[0] <== AddMaxbitTag(needed_bits)(in + n); // to ensure that 0 <= aux[0] < 2**nbits(2 * n) 247 | aux[1] <== 2 * n; 248 | 249 | signal out1 <== LessEqThan(n)(aux); // checks that 0 <= in + n <= 2 * n <==> -n <= in <= n 250 | out1 === 1; 251 | 252 | out.max_abs = n; 253 | out <== in; 254 | } 255 | 256 | 257 | -------------------------------------------------------------------------------- /circuits/lib/utils/treeUtils.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../node_modules/circomlib/circuits/bitify.circom"; 4 | include "../../../node_modules/circomlib/circuits/comparators.circom"; 5 | include "../../../node_modules/circomlib/circuits/eddsaposeidon.circom"; 6 | include "../../../node_modules/circomlib/circuits/smt/smtverifier.circom"; 7 | include "../../../node_modules/circomlib/circuits/mux3.circom"; 8 | include "../../../node_modules/circomlib/circuits/mux1.circom"; 9 | include "claimUtils.circom"; 10 | 11 | // checkClaimExists verifies that claim is included into the claim tree root 12 | template checkClaimExists(IssuerLevels) { 13 | signal input {binary} enabled; 14 | signal input claimHi; 15 | signal input claimHv; 16 | signal input claimMTP[IssuerLevels]; 17 | signal input treeRoot; 18 | 19 | SMTVerifier(IssuerLevels)( 20 | enabled <== enabled, // enabled 21 | root <== treeRoot, // root 22 | siblings <== claimMTP, // siblings 23 | oldKey <== 0, // oldKey 24 | oldValue <== 0, // oldValue 25 | isOld0 <== 0, // isOld0 26 | key <== claimHi, // key 27 | value <== claimHv, // value 28 | fnc <== 0 // fnc = inclusion 29 | ); 30 | } 31 | 32 | template checkClaimNotRevoked(treeLevels) { 33 | signal input {binary} enabled; 34 | signal input claim[8]; 35 | signal input claimNonRevMTP[treeLevels]; 36 | signal input treeRoot; 37 | signal input noAux; 38 | signal input auxHi; 39 | signal input auxHv; 40 | 41 | signal claimRevNonce <== getClaimRevNonce()(claim); 42 | 43 | SMTVerifier(treeLevels)( 44 | enabled <== enabled, 45 | root <== treeRoot, 46 | siblings <== claimNonRevMTP, 47 | oldKey <== auxHi, 48 | oldValue <== auxHv, 49 | isOld0 <== noAux, 50 | key <== claimRevNonce, 51 | value <== 0, 52 | fnc <== 1 // Non-inclusion 53 | ); 54 | } 55 | 56 | // checkIdenStateMatchesRoots checks that a hash of 3 tree 57 | // roots is equal to expected identity state 58 | template checkIdenStateMatchesRoots() { 59 | signal input {binary} enabled; 60 | signal input claimsTreeRoot; 61 | signal input revTreeRoot; 62 | signal input rootsTreeRoot; 63 | signal input expectedState; 64 | 65 | signal idenState <== getIdenState()( 66 | claimsTreeRoot, 67 | revTreeRoot, 68 | rootsTreeRoot 69 | ); 70 | 71 | ForceEqualIfEnabled()( 72 | enabled, 73 | [idenState, expectedState] 74 | ); 75 | } 76 | 77 | // verifyClaimIssuance verifies that claim is issued by the issuer 78 | template verifyClaimIssuance(IssuerLevels) { 79 | signal input {binary} enabled; 80 | signal input claimHi; 81 | signal input claimHv; 82 | signal input claimIssuanceMtp[IssuerLevels]; 83 | signal input claimIssuanceClaimsTreeRoot; 84 | signal input claimIssuanceRevTreeRoot; 85 | signal input claimIssuanceRootsTreeRoot; 86 | signal input claimIssuanceIdenState; 87 | 88 | // verify country claim is included in claims tree root 89 | checkClaimExists(IssuerLevels)( 90 | enabled, 91 | claimHi, 92 | claimHv, 93 | claimIssuanceMtp, 94 | claimIssuanceClaimsTreeRoot 95 | ); 96 | 97 | // verify issuer state includes country claim 98 | checkIdenStateMatchesRoots()( 99 | enabled, 100 | claimIssuanceClaimsTreeRoot, 101 | claimIssuanceRevTreeRoot, 102 | claimIssuanceRootsTreeRoot, 103 | claimIssuanceIdenState 104 | ); 105 | } 106 | 107 | template VerifyAuthClaimAndSignature(nLevels) { 108 | signal input {binary} enabled; 109 | 110 | signal input claimsTreeRoot; 111 | signal input authClaimMtp[nLevels]; 112 | signal input authClaim[8]; 113 | 114 | signal input revTreeRoot; 115 | signal input authClaimNonRevMtp[nLevels]; 116 | signal input authClaimNonRevMtpNoAux; 117 | signal input authClaimNonRevMtpAuxHi; 118 | signal input authClaimNonRevMtpAuxHv; 119 | 120 | signal input challenge; 121 | signal input challengeSignatureR8x; 122 | signal input challengeSignatureR8y; 123 | signal input challengeSignatureS; 124 | 125 | component authClaimHeader = getClaimHeader(); 126 | authClaimHeader.claim <== authClaim; 127 | 128 | // AuthHash cca3371a6cb1b715004407e325bd993c 129 | // BigInt: 80551937543569765027552589160822318028 130 | // https://schema.iden3.io/core/jsonld/auth.jsonld#AuthBJJCredential 131 | verifyCredentialSchema()( 132 | enabled, 133 | authClaimHeader.schema, 134 | 80551937543569765027552589160822318028 135 | ); 136 | 137 | signal authClaimHi, authClaimHv; 138 | (authClaimHi, authClaimHv) <== getClaimHiHv()(authClaim); 139 | 140 | checkClaimExists(nLevels)( 141 | enabled, 142 | authClaimHi, 143 | authClaimHv, 144 | authClaimMtp, 145 | claimsTreeRoot 146 | ); 147 | 148 | checkClaimNotRevoked(nLevels)( 149 | enabled, 150 | authClaim, 151 | authClaimNonRevMtp, 152 | revTreeRoot, 153 | authClaimNonRevMtpNoAux, 154 | authClaimNonRevMtpAuxHi, 155 | authClaimNonRevMtpAuxHv 156 | ); 157 | 158 | checkDataSignatureWithPubKeyInClaim()( 159 | enabled, 160 | authClaim, 161 | challengeSignatureS, 162 | challengeSignatureR8x, 163 | challengeSignatureR8y, 164 | challenge 165 | ); 166 | 167 | // explicitly state that some of these signals are not used and it's ok 168 | for (var i=0; i<32; i++) { 169 | _ <== authClaimHeader.claimFlags[i]; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /circuits/linked/multiQuery.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../node_modules/circomlib/circuits/comparators.circom"; 4 | include "../lib/query/processQueryWithModifiers.circom"; 5 | include "../lib/linked/linkId.circom"; 6 | include "../lib/utils/claimUtils.circom"; 7 | include "../lib/utils/safeOne.circom"; 8 | include "../lib/utils/queryHash.circom"; 9 | 10 | // This circuit processes multiple query requests at once for a given claim using linked proof 11 | template LinkedMultiQuery(N, claimLevels, maxValueArraySize) { 12 | 13 | // linked proof signals 14 | signal input linkNonce; 15 | signal input issuerClaim[8]; 16 | 17 | // query signals 18 | signal input claimSchema; 19 | signal input claimPathMtp[N][claimLevels]; 20 | signal input claimPathMtpNoAux[N]; // 1 if aux node is empty, 0 if non-empty or for inclusion proofs 21 | signal input claimPathMtpAuxHi[N]; // 0 for inclusion proof 22 | signal input claimPathMtpAuxHv[N]; // 0 for inclusion proof 23 | signal input claimPathKey[N]; // hash of path in merklized json-ld document 24 | signal input claimPathValue[N]; // value in this path in merklized json-ld document 25 | signal input slotIndex[N]; 26 | signal input operator[N]; 27 | signal input value[N][maxValueArraySize]; 28 | signal input valueArraySize[N]; 29 | 30 | // Outputs 31 | signal output linkID; 32 | signal output merklized; 33 | signal output operatorOutput[N]; 34 | signal output circuitQueryHash[N]; 35 | 36 | ///////////////////////////////////////////////////////////////// 37 | // General verifications 38 | ///////////////////////////////////////////////////////////////// 39 | 40 | // get safe one values to be used in ForceEqualIfEnabled 41 | signal {binary} one <== SafeOne()(linkNonce); // 7 constraints 42 | 43 | // get claim header 44 | component issuerClaimHeader = getClaimHeader(); // 300 constraints 45 | issuerClaimHeader.claim <== issuerClaim; 46 | 47 | // get merklized flag & root 48 | component merklize = getClaimMerklizeRoot(); 49 | merklize.claim <== issuerClaim; 50 | merklize.claimFlags <== issuerClaimHeader.claimFlags; 51 | 52 | merklized <== merklize.flag; 53 | 54 | // Verify issuerClaim schema 55 | verifyCredentialSchema()(one, issuerClaimHeader.schema, claimSchema); // 3 constraints 56 | 57 | signal issuerClaimHash, issuerClaimHi, issuerClaimHv; 58 | (issuerClaimHash, issuerClaimHi, issuerClaimHv) <== getClaimHash()(issuerClaim); // 834 constraints 59 | //////////////////////////////////////////////////////////////////////// 60 | // calculate linkID 61 | //////////////////////////////////////////////////////////////////////// 62 | linkID <== LinkID()(issuerClaimHash, linkNonce); // 243 constraints 63 | 64 | 65 | signal operatorNotNoop[N]; 66 | ///////////////////////////////////////////////////////////////// 67 | // Query Processing Loop 68 | ///////////////////////////////////////////////////////////////// 69 | for (var i=0; i info.txt 15 | # git show --summary >> info.txt 16 | # fi 17 | 18 | 19 | cp "$CIRCUIT_PATH" circuit.circom 20 | 21 | set -x 22 | time circom --inspect --r1cs --wasm --c --sym "$CIRCUIT_PATH" 23 | mv "${CIRCUIT}.r1cs" circuit.r1cs 24 | mv "${CIRCUIT}_js/${CIRCUIT}.wasm" circuit.wasm 25 | mv "${CIRCUIT}.sym" circuit.sym 26 | snarkjs r1cs info circuit.r1cs 27 | #snarkjs r1cs export json circuit.r1cs circuit.r1cs.json 28 | 29 | # time snarkjs setup -r circuit.r1cs --pk proving_key.json --vk verification_key.json 30 | time snarkjs groth16 setup circuit.r1cs "$PTAU" circuit_0000.zkey 31 | 32 | ENTROPY1=$(head -c 64 /dev/urandom | od -An -tx1 -v | tr -d ' \n') 33 | ENTROPY2=$(head -c 64 /dev/urandom | od -An -tx1 -v | tr -d ' \n') 34 | ENTROPY3=$(head -c 64 /dev/urandom | od -An -tx1 -v | tr -d ' \n') 35 | 36 | # time snarkjs zkey contribute circuit_0000.zkey circuit_0001.zkey --name="1st Contribution" -v -e="$ENTROPY1" 37 | # time snarkjs zkey contribute circuit_0001.zkey circuit_0002.zkey --name="2nd Contribution" -v -e="$ENTROPY2" 38 | # time snarkjs zkey contribute circuit_0002.zkey circuit_0003.zkey --name="3rd Contribution" -v -e="$ENTROPY3" 39 | # time snarkjs zkey verify circuit.r1cs "$PTAU" circuit_0003.zkey 40 | # time snarkjs zkey beacon circuit_0003.zkey circuit_final.zkey 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon phase2" 41 | 42 | time snarkjs zkey contribute circuit_0000.zkey circuit_final.zkey --name="1st Contribution" -v -e="$ENTROPY1" 43 | # time snarkjs zkey verify circuit.r1cs "$PTAU" circuit_final.zkey 44 | time snarkjs zkey export verificationkey circuit_final.zkey verification_key.json 45 | #time snarkjs zkey export json circuit_final.zkey circuit_final.zkey.json 46 | 47 | time snarkjs zkey export solidityverifier circuit_final.zkey verifier.sol 48 | set +x 49 | } 50 | 51 | if [ "$#" -ne 2 ] 52 | then 53 | echo "Usage: $0 CIRCUIT_PATH $1 PTAU_PATH">&2 54 | echo "Example: ./compile-circuit.sh example.circom powersOfTau28_hez_final_15.ptau" >&2 55 | exit 1 56 | fi 57 | 58 | set -u 59 | 60 | CIRCUIT="$(pwd)/$1" 61 | PTAU="$(pwd)/$2" 62 | 63 | # npm ci 64 | mkdir -p build 65 | 66 | cd build 67 | compile_and_ts "$CIRCUIT" 68 | -------------------------------------------------------------------------------- /generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | generate_proof() { 6 | CIRCUIT="$(pwd)/build/$1" 7 | CIRCUIT_JS="$(pwd)/build/$1/$1_js" 8 | 9 | node $CIRCUIT_JS/generate_witness.js $CIRCUIT/circuit.wasm $CIRCUIT_JS/input.json $CIRCUIT_JS/witness.wtns 10 | 11 | snarkjs groth16 prove $CIRCUIT/circuit_final.zkey $CIRCUIT_JS/witness.wtns $CIRCUIT_JS/proof.json $CIRCUIT_JS/public.json 12 | 13 | snarkjs groth16 verify $CIRCUIT/verification_key.json $CIRCUIT_JS/public.json $CIRCUIT_JS/proof.json 14 | } 15 | 16 | generate_proof $1 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@iden3/circuits", 3 | "version": "0.0.1", 4 | "description": "iden3 circuits", 5 | "main": "index.js", 6 | "scripts": { 7 | "clean": "rm -fR dist", 8 | "build": "npm run clean && ./node_modules/.bin/tsc --strictNullChecks", 9 | "test": "./node_modules/.bin/mocha --timeout 5000 -p -r ts-node/register 'test/**/*.test.ts'" 10 | }, 11 | "license": "GPL-3.0", 12 | "dependencies": { 13 | "circomlib": "^2.0.5" 14 | }, 15 | "devDependencies": { 16 | "@types/chai": "^4.3.5", 17 | "@types/mocha": "^10.0.1", 18 | "@types/node": "^20.5.7", 19 | "adm-zip": "^0.5.10", 20 | "aws-sdk": "^2.1447.0", 21 | "circom_tester": "^0.0.20", 22 | "circomlibjs": "v0.1.7", 23 | "chai": "^4.3.8", 24 | "mocha": "^10.2.0", 25 | "mocha-steps": "^1.3.0", 26 | "ts-node": "^10.9.1", 27 | "eslint": "^8.48.0", 28 | "typescript": "^5.2.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /s3_util.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const AdmZip = require('adm-zip'); 3 | const path = require('path'); 4 | 5 | const BUCKET_NAME = 'iden3-circuits-bucket'; 6 | const FILES_TO_INCLUDE = ['circuit.wasm', 'circuit_final.zkey', 'verification_key.json']; 7 | const FOLDER_TO_COMPRESS = './build'; 8 | const OPERATIONS = ['add', 'rm', 'zip']; 9 | 10 | function main() { 11 | const [operation, zipName] = parseArgs(); 12 | processOperation(operation, zipName); 13 | } 14 | 15 | main(); 16 | 17 | function processOperation(operation, zipName) { 18 | try { 19 | switch (operation) { 20 | case 'add': uploadZipFile(zipName) 21 | break; 22 | case 'rm': deleteFile(zipName) 23 | break; 24 | case 'zip': 25 | const zip = makeZip(zipName) 26 | console.log(`Starting saving zip...`); 27 | zip.writeZip(zipName); 28 | console.log(`Zip successfully saved: ${path.join(process.cwd(), zipName)}`); 29 | break; 30 | default: 31 | console.error(`Operation ${operation} is not supported`) 32 | break; 33 | } 34 | } catch (error) { 35 | console.error(error) 36 | process.exit(1) 37 | } 38 | } 39 | 40 | function parseArgs() { 41 | let [operation, zipName] = process.argv.slice(2); 42 | 43 | if (!OPERATIONS.includes(operation)) { 44 | console.error(`Error! Supported operations: ${OPERATIONS.join(', ')}`); 45 | process.exit(1) 46 | } 47 | 48 | if (!zipName) { 49 | console.error(`please provide name for zip file as argument, for example 'node s3_upload.js add v1.zip'`) 50 | process.exit(1) 51 | } 52 | 53 | zipName = !zipName.includes('.zip') ? `${zipName}.zip` : zipName; 54 | 55 | return [operation, zipName]; 56 | }; 57 | 58 | function uploadZipFile(zipName) { 59 | const s3 = new AWS.S3({ 60 | accessKeyId: process.env.ACCESS_KEY_ID, 61 | secretAccessKey: process.env.SECRET_ACCESS_KEY 62 | }); 63 | 64 | const zip = makeZip() 65 | 66 | console.log('Creating zip buffer...'); 67 | const params = { 68 | Bucket: BUCKET_NAME, 69 | Key: zipName, 70 | Body: zip.toBuffer() 71 | }; 72 | console.log('Buffer successfully created') 73 | 74 | // Uploading files to the bucket 75 | const uploading = s3.upload(params, function (err, data) { 76 | if (err) { 77 | console.error(err.message ?? err); 78 | throw err; 79 | } 80 | console.info(`Zip file uploaded successfully. ${data.Location}`); 81 | }); 82 | 83 | trackProgressFor(uploading); 84 | }; 85 | 86 | 87 | function makeZip() { 88 | console.info(`Starting creation of zip file...`); 89 | const zip = new AdmZip(); 90 | zip.addLocalFolder(FOLDER_TO_COMPRESS, '', name => FILES_TO_INCLUDE.some(ext => name.endsWith(ext))); 91 | return zip; 92 | } 93 | 94 | function deleteFile(zipName) { 95 | console.info(`Starting deleting of ${zipName} from ${BUCKET_NAME}...`); 96 | 97 | const s3 = new AWS.S3({ 98 | accessKeyId: process.env.ACCESS_KEY_ID, 99 | secretAccessKey: process.env.SECRET_ACCESS_KEY 100 | }); 101 | 102 | // Setting up S3 upload parameters 103 | const params = { 104 | Bucket: BUCKET_NAME, 105 | Key: zipName 106 | }; 107 | 108 | // Uploading files to the bucket 109 | const deletion = s3.deleteObject(params, function (err, data) { 110 | if (err) { 111 | console.error(err.message ?? err); 112 | throw err; 113 | } 114 | console.log(`File ${zipName} successfully deleted.`); 115 | }); 116 | 117 | trackProgressFor(deletion); 118 | } 119 | 120 | function trackProgressFor(source) { 121 | source.on('httpUploadProgress', ({ loaded, total }) => { 122 | if (loaded && total) console.log(`Progress: ${Math.floor(loaded / total * 100)}%`); 123 | }); 124 | } 125 | -------------------------------------------------------------------------------- /scripts/bulk_rename_text_occurrences.sh: -------------------------------------------------------------------------------- 1 | 2 | ### Put your repo root folder path here !!!! ### 3 | ROOT_FOLDER="" 4 | cd $ROOT_FOLDER || exit 5 | 6 | # The list of words to search and replace 7 | # Note: it searches exactly for words but not text occurrences 8 | # so for the word "foo" it will find it in "foo > 2" or "x.foo = 1" text but will not 9 | # find anything in "fool > 2" 10 | OCCURENCE_AND_REPLACE_FEED=( 11 | "firstWordToSearchForReplacement" "theWordToReplaceTheFirstWord" 12 | "secondWordToSearchForReplacement" "theWordToReplaceTheSecondWord" 13 | "..." "..." 14 | ); 15 | 16 | # This is a list of files (file pathes relative to the root folder) and their regex adjustments. 17 | # The regex adjustment we need to put before the words we search. 18 | # For example: 19 | # For the adjustment "\." and word to search "foo" the search will 20 | # find ".foo" in "someCircuitName.foo" text but will not find anythin in "var x=foo" 21 | # for the adjustment "" it will find the word "foo" anywhere 22 | FILES_AND_REGEX_TO_PROCESS=( 23 | "circuits/lib/idOwnershipBySignature.circom" " " # will find "x.input1 <== foo" but not "x.foo" 24 | "circuits/lib/query/credentialAtomicQueryMTP.circom" "\." # will find "x.foo" but not "x.input1 <== foo" 25 | "circuits/lib/auth.circom" "\." 26 | "test/idOwnership/idOwnershipBySignature.test.ts" "" # will find "foo" word in any place 27 | "test/circuits/idOwnershipBySignature.circom" "" 28 | ); 29 | 30 | OCCURENCE_AND_REPLACE_FEED_LENGTH=${#OCCURENCE_AND_REPLACE_FEED} 31 | 32 | for i in $(seq 1 2 $OCCURENCE_AND_REPLACE_FEED_LENGTH); 33 | do 34 | next_i=$((i+1)) 35 | OCCURRENCE_TO_REPLACE=${OCCURENCE_AND_REPLACE_FEED[$i]} 36 | NEW_OCCURRENCE=${OCCURENCE_AND_REPLACE_FEED[$next_i]} 37 | 38 | for m in $(seq 1 2 ${#FILES_AND_REGEX_TO_PROCESS}); 39 | do 40 | next_m=$((m+1)) 41 | FILE_PATH=$ROOT_FOLDER${FILES_AND_REGEX_TO_PROCESS[$m]} 42 | REGEX_ADJUSTMENT=${FILES_AND_REGEX_TO_PROCESS[$m+1]} 43 | sed -i'.bak' "s/"$REGEX_ADJUSTMENT"[[:<:]]"$OCCURRENCE_TO_REPLACE"[[:>:]]/"$REGEX_ADJUSTMENT$NEW_OCCURRENCE"/g" $FILE_PATH 44 | rm $FILE_PATH".bak" 45 | done 46 | done 47 | 48 | git status 49 | #git checkout HEAD ./circuits/**/*.circom ./test/circuits/**/*.circom ./test/**/*.test.ts 50 | 51 | -------------------------------------------------------------------------------- /test/auth/authV3.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | // inputs MUST be generated by GO-CIRCUITS library https://github.com/iden3/go-circuits (using corresponding test) 7 | describe("authV3Test.circom:", async function() { 8 | 9 | const tests = [ 10 | {"desc":"Ownership true. User state: not-genesis. Auth claims total/signedWith/revoked: 1/1/none","inputs":{"genesisID":"23148936466334350744548790012294489365207440754509988986684797708370051073","profileNonce":"0","authClaim":["80551937543569765027552589160822318028","0","4720763745722683616702324599137259461509439547324750011830105416383780791263","4844030361230692908091131578688419341633213823133966379083981236400104720538","16547485850637761685","0","0","0"],"authClaimIncMtp":["20643387758736831799596675626240785455902781070167728593409367019626753600795","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"authClaimNonRevMtp":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"authClaimNonRevMtpAuxHi":"0","authClaimNonRevMtpAuxHv":"0","authClaimNonRevMtpNoAux":"1","challenge":"12345","challengeSignatureR8x":"15829360093371098546177008474519342171461782120259125067189481965541223738777","challengeSignatureR8y":"10840522802382821290541462398953040493080116495308402635486440290351677745960","challengeSignatureS":"1196477404779941775725836688033485533497812196897664950083199167075327114562","claimsTreeRoot":"8794724428328826645726823821449086761079599815895679828313419678997386356573","revTreeRoot":"0","rootsTreeRoot":"0","state":"7115004997868594253010848596868364067574661249707337517331323113105592633327","gistRoot":"12426001693315048096465296555250933925657269666213597651273856698420831593981","gistMtp":["0","0","0","0","1243904711429961858774220647610724273798918457991486031567244100767259239747","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"gistMtpAuxHi":"0","gistMtpAuxHv":"0","gistMtpNoAux":"0"},"expOut":{"userID":"23148936466334350744548790012294489365207440754509988986684797708370051073","gistRoot":"12426001693315048096465296555250933925657269666213597651273856698420831593981","challenge":"12345"}}, 11 | {"desc":"Ownership true. User state: not-genesis. Auth claims total/signedWith/revoked: 1/1/none","inputs":{"genesisID":"23148936466334350744548790012294489365207440754509988986684797708370051073","profileNonce":"0","authClaim":["80551937543569765027552589160822318028","0","18843627616807347027405965102907494712213509184168391784663804560181782095821","21769574296201138406688395494914474950554632404504713590270198507141791084591","17476719578317212277","0","0","0"],"authClaimIncMtp":["8162166103065016664685834856644195001371303013149727027131225893397958846382","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"authClaimNonRevMtp":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"authClaimNonRevMtpAuxHi":"16547485850637761685","authClaimNonRevMtpAuxHv":"0","authClaimNonRevMtpNoAux":"0","challenge":"12345","challengeSignatureR8x":"17119525341148708510056742833108899809180137847226842265134929121642912372281","challengeSignatureR8y":"14361124785409490066314019246273984594356444175220864488356627192969301706799","challengeSignatureS":"1437929958210592098523189041037049993330511094749287599959220159702091719018","claimsTreeRoot":"8794724428328826645726823821449086761079599815895679828313419678997386356573","revTreeRoot":"18174590471735654296853614985726184006995378344929215927298747263240370223984","rootsTreeRoot":"0","state":"11011081180322189554242336567873361504785021441826614473690174477816587629954","gistRoot":"2372526788462776994418746206668460343891099414664260600394307379551521456907","gistMtp":["0","0","0","0","1243904711429961858774220647610724273798918457991486031567244100767259239747","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"gistMtpAuxHi":"0","gistMtpAuxHv":"0","gistMtpNoAux":"0"},"expOut":{"userID":"23148936466334350744548790012294489365207440754509988986684797708370051073","gistRoot":"2372526788462776994418746206668460343891099414664260600394307379551521456907","challenge":"12345"}}, 12 | {"desc":"Ownership true. User state: genesis. Auth claims total/signedWith/revoked: 1/1/none","inputs":{"genesisID":"23148936466334350744548790012294489365207440754509988986684797708370051073","profileNonce":"0","authClaim":["80551937543569765027552589160822318028","0","4720763745722683616702324599137259461509439547324750011830105416383780791263","4844030361230692908091131578688419341633213823133966379083981236400104720538","16547485850637761685","0","0","0"],"authClaimIncMtp":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"authClaimNonRevMtp":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"authClaimNonRevMtpAuxHi":"0","authClaimNonRevMtpAuxHv":"0","authClaimNonRevMtpNoAux":"1","challenge":"12345","challengeSignatureR8x":"15829360093371098546177008474519342171461782120259125067189481965541223738777","challengeSignatureR8y":"10840522802382821290541462398953040493080116495308402635486440290351677745960","challengeSignatureS":"1196477404779941775725836688033485533497812196897664950083199167075327114562","claimsTreeRoot":"8162166103065016664685834856644195001371303013149727027131225893397958846382","revTreeRoot":"0","rootsTreeRoot":"0","state":"8039964009611210398788855768060749920589777058607598891238307089541758339342","gistRoot":"1243904711429961858774220647610724273798918457991486031567244100767259239747","gistMtp":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"gistMtpAuxHi":"1","gistMtpAuxHv":"1","gistMtpNoAux":"0"},"expOut":{"userID":"23148936466334350744548790012294489365207440754509988986684797708370051073","gistRoot":"1243904711429961858774220647610724273798918457991486031567244100767259239747","challenge":"12345"}}, 13 | {"desc":"nonce=10. ProfileID == UserID should be true. Ownership true. User state: genesis. Auth claims total/signedWith/revoked: 1/1/none","inputs":{"genesisID":"23148936466334350744548790012294489365207440754509988986684797708370051073","profileNonce":"10","authClaim":["80551937543569765027552589160822318028","0","4720763745722683616702324599137259461509439547324750011830105416383780791263","4844030361230692908091131578688419341633213823133966379083981236400104720538","16547485850637761685","0","0","0"],"authClaimIncMtp":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"authClaimNonRevMtp":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"authClaimNonRevMtpAuxHi":"0","authClaimNonRevMtpAuxHv":"0","authClaimNonRevMtpNoAux":"1","challenge":"12345","challengeSignatureR8x":"15829360093371098546177008474519342171461782120259125067189481965541223738777","challengeSignatureR8y":"10840522802382821290541462398953040493080116495308402635486440290351677745960","challengeSignatureS":"1196477404779941775725836688033485533497812196897664950083199167075327114562","claimsTreeRoot":"8162166103065016664685834856644195001371303013149727027131225893397958846382","revTreeRoot":"0","rootsTreeRoot":"0","state":"8039964009611210398788855768060749920589777058607598891238307089541758339342","gistRoot":"1243904711429961858774220647610724273798918457991486031567244100767259239747","gistMtp":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],"gistMtpAuxHi":"1","gistMtpAuxHv":"1","gistMtpNoAux":"0"},"expOut":{"userID":"19816097857299506276751016592539988756969255304244082727801276212869922817","gistRoot":"1243904711429961858774220647610724273798918457991486031567244100767259239747","challenge":"12345"}}, 14 | ]; 15 | 16 | let circuit; 17 | this.timeout(300000) 18 | 19 | before(async () => { 20 | circuit = await wasm_tester( 21 | path.join(__dirname, "../circuits", "authV3Test.circom"), 22 | { 23 | output: path.join(__dirname, "../circuits", "build/authV3"), 24 | recompile: true, 25 | }, 26 | ); 27 | }); 28 | 29 | tests.forEach(({desc, inputs, expOut}) => { 30 | it(`auth ${desc}`, async function() { 31 | const w = await circuit.calculateWitness(inputs, true); 32 | await circuit.checkConstraints(w); 33 | await circuit.assertOut(w, expOut); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/circuits/authV3Test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/auth/authV3.circom"; 4 | 5 | component main {public [challenge, gistRoot]} = AuthV3(32,32); 6 | -------------------------------------------------------------------------------- /test/circuits/comparators.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/query/comparators.circom"; 4 | 5 | component main = LessThan254(); 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/circuits/comparators_greater_than.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/query/comparators.circom"; 4 | 5 | component main = GreaterThan254(); 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/circuits/eddsaposeidon.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../node_modules/circomlib/circuits/eddsaposeidon.circom"; 4 | 5 | template EdDSAPoseidonTest() { 6 | signal input enabled; 7 | signal input Ax; 8 | signal input Ay; 9 | 10 | signal input S; 11 | signal input R8x; 12 | signal input R8y; 13 | 14 | signal input M; 15 | 16 | EdDSAPoseidonVerifier()( 17 | enabled, 18 | Ax, 19 | Ay, 20 | S, 21 | R8x, 22 | R8y, 23 | M 24 | ); 25 | } 26 | 27 | component main = EdDSAPoseidonTest(); 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /test/circuits/eq.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | //include "../../circuits/comparators.circom"; 4 | include "../../node_modules/circomlib/circuits/comparators.circom"; 5 | 6 | template EqWithLog(n) { 7 | signal input in[2]; 8 | signal output out; 9 | 10 | log(in[0]); 11 | log(in[1]); 12 | 13 | component eq = IsEqual(); 14 | eq.in[0] <== in[0]; 15 | eq.in[1] <== in[1]; 16 | out <== eq.out; 17 | } 18 | 19 | component main = EqWithLog(252); 20 | 21 | -------------------------------------------------------------------------------- /test/circuits/idUtils_CalculateIdChecksum.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/utils/idUtils.circom"; 4 | 5 | component main = CalculateIdChecksum(); 6 | -------------------------------------------------------------------------------- /test/circuits/idUtils_GatherID.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/utils/idUtils.circom"; 4 | 5 | component main = GatherID(); 6 | -------------------------------------------------------------------------------- /test/circuits/idUtils_NewID.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/utils/idUtils.circom"; 4 | 5 | component main = NewID(); 6 | -------------------------------------------------------------------------------- /test/circuits/idUtils_ProfileID.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/utils/idUtils.circom"; 4 | 5 | component main = ProfileID(); 6 | -------------------------------------------------------------------------------- /test/circuits/idUtils_SelectProfile.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/utils/idUtils.circom"; 4 | 5 | component main = SelectProfile(); 6 | -------------------------------------------------------------------------------- /test/circuits/idUtils_SplitID.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/utils/idUtils.circom"; 4 | 5 | component main = SplitID(); 6 | -------------------------------------------------------------------------------- /test/circuits/idUtils_TakeNBits.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/utils/idUtils.circom"; 4 | 5 | component main = TakeNBits(80); 6 | -------------------------------------------------------------------------------- /test/circuits/lessthan.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/query/comparators.circom"; 4 | //include "../../node_modules/circomlib/circuits/comparators.circom"; 5 | 6 | template LessThanWithLog() { 7 | signal input in[2]; 8 | signal output out; 9 | 10 | log(in[0]); 11 | log(in[1]); 12 | 13 | component lt = LessThan254(); 14 | lt.in[0] <== in[0]; 15 | lt.in[1] <== in[1]; 16 | out <== lt.out; 17 | } 18 | 19 | component main = LessThanWithLog(); 20 | 21 | -------------------------------------------------------------------------------- /test/circuits/poseidon.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../node_modules/circomlib/circuits/poseidon.circom"; 4 | 5 | template PoseidonTest() { 6 | signal input in[3]; 7 | signal output out; 8 | 9 | component h = Poseidon(3); 10 | h.inputs[0] <== in[0]; 11 | h.inputs[1] <== in[1]; 12 | h.inputs[2] <== in[2]; 13 | 14 | out <== h.out; 15 | } 16 | component main = PoseidonTest(); 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/circuits/poseidon14.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../node_modules/circomlib/circuits/poseidon.circom"; 4 | 5 | template PoseidonTest() { 6 | signal input in[14]; 7 | signal output out; 8 | 9 | component h = Poseidon(14); 10 | h.inputs[0] <== in[0]; 11 | h.inputs[1] <== in[1]; 12 | h.inputs[2] <== in[2]; 13 | h.inputs[3] <== in[3]; 14 | h.inputs[4] <== in[4]; 15 | h.inputs[5] <== in[5]; 16 | h.inputs[6] <== in[6]; 17 | h.inputs[7] <== in[7]; 18 | h.inputs[8] <== in[8]; 19 | h.inputs[9] <== in[9]; 20 | h.inputs[10] <== in[10]; 21 | h.inputs[11] <== in[11]; 22 | h.inputs[12] <== in[12]; 23 | h.inputs[13] <== in[13]; 24 | //h.inputs[14] <== in[14]; 25 | //h.inputs[15] <== in[15]; 26 | 27 | out <== h.out; 28 | } 29 | component main = PoseidonTest(); 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/circuits/poseidon16.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../node_modules/circomlib/circuits/poseidon.circom"; 4 | 5 | template PoseidonTest() { 6 | signal input in[16]; 7 | signal output out; 8 | 9 | component h = Poseidon(16); 10 | h.inputs[0] <== in[0]; 11 | h.inputs[1] <== in[1]; 12 | h.inputs[2] <== in[2]; 13 | h.inputs[3] <== in[3]; 14 | h.inputs[4] <== in[4]; 15 | h.inputs[5] <== in[5]; 16 | h.inputs[6] <== in[6]; 17 | h.inputs[7] <== in[7]; 18 | h.inputs[8] <== in[8]; 19 | h.inputs[9] <== in[9]; 20 | h.inputs[10] <== in[10]; 21 | h.inputs[11] <== in[11]; 22 | h.inputs[12] <== in[12]; 23 | h.inputs[13] <== in[13]; 24 | h.inputs[14] <== in[14]; 25 | h.inputs[15] <== in[15]; 26 | 27 | out <== h.out; 28 | } 29 | component main = PoseidonTest(); 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/circuits/query/inTest.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/query/comparators.circom"; 4 | 5 | component main = IN(3); 6 | -------------------------------------------------------------------------------- /test/circuits/query/nullifyTest.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.5; 2 | 3 | include "../../../circuits/lib/utils/nullify.circom"; 4 | 5 | component main = Nullify(); 6 | -------------------------------------------------------------------------------- /test/circuits/query/queryTest.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/query/query.circom"; 4 | 5 | component main { public [value] } = Query(3); 6 | -------------------------------------------------------------------------------- /test/circuits/stateTransitionTest.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../circuits/lib/stateTransition.circom"; 4 | 5 | component main {public [userID,oldUserState,newUserState,isOldStateGenesis]} = StateTransition(32); 6 | -------------------------------------------------------------------------------- /test/circuits/utils/claimUtils_getClaimHeader.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | 5 | component main = getClaimHeader(); 6 | -------------------------------------------------------------------------------- /test/circuits/utils/claimUtils_getClaimMerklizeRoot.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | include "../../../circuits/lib/utils/tags-managing.circom"; 5 | 6 | template wrappedGetClaimMerklizeRoot() { 7 | signal input claim[8]; 8 | signal input claimFlags[32]; 9 | signal output flag; 10 | signal output out; 11 | 12 | component check = getClaimMerklizeRoot(); 13 | check.claim <== claim; 14 | check.claimFlags <== AddBinaryArrayTag(32)(claimFlags); 15 | flag <== check.flag; 16 | out <== check.out; 17 | } 18 | 19 | component main = wrappedGetClaimMerklizeRoot(); 20 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_GetValueByIndex.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | 5 | component main = getValueByIndex(); 6 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_arraySizeValidatorTest.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/arraySizeValidator.circom"; 4 | 5 | component main = ArraySizeValidator(64); 6 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_checkIdenStateMatchesRoots.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | include "../../../circuits/lib/utils/treeUtils.circom"; 5 | include "../../../circuits/lib/utils/tags-managing.circom"; 6 | 7 | template wrappedCheckIdenStateMatchesRoots(){ 8 | signal input enabled; 9 | signal input claimsTreeRoot; 10 | signal input revTreeRoot; 11 | signal input rootsTreeRoot; 12 | signal input expectedState; 13 | 14 | component check = checkIdenStateMatchesRoots(); 15 | check.enabled <== AddBinaryTag()(enabled); 16 | check.claimsTreeRoot <== claimsTreeRoot; 17 | check.revTreeRoot <== revTreeRoot; 18 | check.rootsTreeRoot <== rootsTreeRoot; 19 | check.expectedState <== expectedState; 20 | } 21 | 22 | component main = wrappedCheckIdenStateMatchesRoots(); 23 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_getClaimExpiration.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | 5 | component main{public[claim]} = getClaimExpiration(); 6 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_getClaimSubjectOtherIden.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | 5 | template getClaimSubjectOtherIdenWrapper() { 6 | signal input claim[8]; 7 | signal output id; 8 | 9 | // get header flags from claim. 10 | component header = getClaimHeader(); 11 | header.claim <== claim; 12 | 13 | id <== getClaimSubjectOtherIden()(claim, header.claimFlags); 14 | } 15 | 16 | component main{public[claim]} = getClaimSubjectOtherIdenWrapper(); 17 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_getSubjectLocation.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | include "../../../circuits/lib/utils/tags-managing.circom"; 5 | 6 | template wrappedGetSubjectLocation() { 7 | signal input claimFlags[32]; 8 | signal output out; 9 | 10 | component check = getSubjectLocation(); 11 | check.claimFlags <== AddBinaryArrayTag(32)(claimFlags); 12 | out <== check.out; 13 | } 14 | 15 | component main = wrappedGetSubjectLocation(); 16 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_isExpirable.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | include "../../../circuits/lib/utils/tags-managing.circom"; 5 | 6 | template wrappedIsExpirable() { 7 | signal input claimFlags[32]; 8 | signal output out; 9 | 10 | component check = isExpirable(); 11 | check.claimFlags <== AddBinaryArrayTag(32)(claimFlags); 12 | out <== check.out; 13 | } 14 | 15 | component main = wrappedIsExpirable(); 16 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_isUpdatable.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | include "../../../circuits/lib/utils/tags-managing.circom"; 5 | 6 | template wrappedIsUpdatable() { 7 | signal input claimFlags[32]; 8 | signal output out; 9 | 10 | component check = isUpdatable(); 11 | check.claimFlags <== AddBinaryArrayTag(32)(claimFlags); 12 | out <== check.out; 13 | } 14 | 15 | component main = wrappedIsUpdatable(); 16 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_verifyClaimSignature.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | include "../../../circuits/lib/utils/tags-managing.circom"; 5 | 6 | template wrappedVerifyClaimSignature(){ 7 | signal input enabled; 8 | signal input claimHash; 9 | signal input sigR8x; 10 | signal input sigR8y; 11 | signal input sigS; 12 | signal input pubKeyX; 13 | signal input pubKeyY; 14 | 15 | component check = verifyClaimSignature(); 16 | check.enabled <== AddBinaryTag()(enabled); 17 | check.claimHash <== claimHash; 18 | check.sigR8x <== sigR8x; 19 | check.sigR8y <== sigR8y; 20 | check.sigS <== sigS; 21 | check.pubKeyX <== pubKeyX; 22 | check.pubKeyY <== pubKeyY; 23 | } 24 | 25 | component main = wrappedVerifyClaimSignature(); 26 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_verifyClaimsTreeRoot.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/treeUtils.circom"; 4 | 5 | component main = verifyClaimsTreeRoot(17); 6 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_verifyCredentialMtp.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | 5 | component main = verifyCredentialMtp(17); 6 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_verifyCredentialMtpHiHv.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | 5 | component main = verifyCredentialMtpHiHv(4); 6 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_verifyCredentialNotRevoked.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/treeUtils.circom"; 4 | 5 | component main = verifyCredentialNotRevoked(17); 6 | -------------------------------------------------------------------------------- /test/circuits/utils/utils_verifyExpirationTime.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.1; 2 | 3 | include "../../../circuits/lib/utils/claimUtils.circom"; 4 | include "../../../circuits/lib/utils/tags-managing.circom"; 5 | 6 | template wrappedVerifyExpirationTime() { 7 | signal input expirationFlag; // claimFlags[3] (expiration flag) is set 8 | signal input claim[8]; 9 | signal input timestamp; 10 | 11 | component check = verifyExpirationTime(); 12 | check.expirationFlag <== AddBinaryTag()(expirationFlag); 13 | check.claim <== claim; 14 | check.timestamp <== timestamp; 15 | } 16 | 17 | component main = wrappedVerifyExpirationTime(); 18 | -------------------------------------------------------------------------------- /test/eddsaposeidon.test.ts: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const tester = require("circom_tester").wasm; 3 | const buildEddsa = require("circomlibjs").buildEddsa; 4 | const buildBabyjub = require("circomlibjs").buildBabyjub; 5 | const chai = require("chai"); 6 | const assert = chai.assert; 7 | 8 | export {}; 9 | 10 | describe("EdDSA Poseidon test", function () { 11 | this.timeout(200000); 12 | let circuit; 13 | let eddsa; 14 | let babyJub; 15 | let F; 16 | 17 | before(async function() { 18 | eddsa = await buildEddsa(); 19 | babyJub = await buildBabyjub(); 20 | F = babyJub.F; 21 | 22 | circuit = await tester(path.join(__dirname, "circuits", "eddsaposeidon.circom")); 23 | }); 24 | 25 | it("Sign a single number", async () => { 26 | 27 | const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex"); 28 | const pubKey = eddsa.prv2pub(prvKey); 29 | 30 | for (var i=0; i<100; i++) { 31 | 32 | const msg = F.e(Math.round(Math.random() * 1e10)); 33 | const signature = eddsa.signPoseidon(prvKey, msg); 34 | 35 | assert(eddsa.verifyPoseidon(msg, signature, pubKey)); 36 | 37 | const input = { 38 | enabled: 1, 39 | Ax: F.toObject(pubKey[0]), 40 | Ay: F.toObject(pubKey[1]), 41 | R8x: F.toObject(signature.R8[0]), 42 | R8y: F.toObject(signature.R8[1]), 43 | S: signature.S, 44 | M: F.toObject(msg) 45 | }; 46 | 47 | // console.log(JSON.stringify(utils.stringifyBigInts(input))); 48 | 49 | const w = await circuit.calculateWitness(input, true); 50 | 51 | await circuit.checkConstraints(w); 52 | } 53 | }); 54 | 55 | }); 56 | 57 | -------------------------------------------------------------------------------- /test/linked/linked.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from "chai"; 2 | import {describe} from "mocha"; 3 | 4 | const path = require("path"); 5 | const wasmTester = require("circom_tester").wasm; 6 | 7 | describe("Test linkedMultiQuery10.circom", function () { 8 | 9 | this.timeout(600000); 10 | 11 | let circuit; 12 | 13 | before(async () => { 14 | circuit = await wasmTester( 15 | path.join(__dirname, "../../circuits", "linkedMultiQuery10.circom"), 16 | { 17 | output: path.join(__dirname, "circuits", "build"), 18 | recompile: true, 19 | }, 20 | ); 21 | 22 | }); 23 | 24 | after(async () => { 25 | circuit.release() 26 | }) 27 | 28 | const basePath = '../../testvectorgen/credentials/linked/testdata/linked' 29 | const tests = [ 30 | 31 | require(`${basePath}/one_query.json`), 32 | require(`${basePath}/two_queries.json`), 33 | 34 | ]; 35 | 36 | tests.forEach(({ desc, inputs, expOut }) => { 37 | it(`${desc}`, async function () { 38 | const w = await circuit.calculateWitness(inputs, true); 39 | await circuit.assertOut(w, expOut); 40 | await circuit.checkConstraints(w); 41 | }); 42 | }); 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /test/negativeNumbers.test.ts: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const wasm_tester = require("circom_tester").wasm; 4 | const c_tester = require("circom_tester").c; 5 | 6 | // Negative numbers are transformed to positive, -x == p-x 7 | // p == 21888242871839275222246405745257275088548364400416034343698204186575808495617 8 | // Wasm witness calculator calculates negative numbers 9 | const test_vectors = { 10 | "0 == p": { 11 | "in": ["0", "21888242871839275222246405745257275088548364400416034343698204186575808495617"], 12 | }, 13 | "1 == p+1 overflow": { 14 | "in": ["1", "21888242871839275222246405745257275088548364400416034343698204186575808495618"], 15 | }, 16 | "-1 == p-1": { 17 | "in": ["-1", "21888242871839275222246405745257275088548364400416034343698204186575808495616"], 18 | }, 19 | "-max(uint32) == p-max(uint32)": { // abs value of number fits into uint32 20 | "in": ["-4294967295", "21888242871839275222246405745257275088548364400416034343698204186571513528322"], 21 | }, 22 | "-(max(uint32)-10) == p-(max(uint32)-10)": { // abs value of number fits into uint32 23 | "in": ["-4294967285", "21888242871839275222246405745257275088548364400416034343698204186571513528332"], 24 | }, 25 | "-(max(uint32)+1) == p-(max(uint32)+1)": { // (max(uint32)+1) will not fit into uint32, so it becomes uint64 26 | "in": ["-4294967296", "21888242871839275222246405745257275088548364400416034343698204186571513528321"], 27 | }, 28 | "-(max(uint64)) == p-(max(uint64))": { // max(uint64) fits into uint64 29 | "in": ["-18446744073709551615", "21888242871839275222246405745257275088548364400416034343679757442502098944002"], 30 | }, 31 | "-(max(uint64)+1) == p-(max(uint64)+1)": { // (max(uint64)+1) will not fit into uint64, so it becomes uint96 32 | "in": ["-18446744073709551616", "21888242871839275222246405745257275088548364400416034343679757442502098944001"], 33 | }, 34 | "-(max(uint96)+1) == p-(max(uint96)+1)": { // (max(uint64)+1) will not fit into uint64, so it becomes uint96 35 | "in": ["-79228162514264337593543950337", "21888242871839275222246405745257275088548364400336806181183939848982264545280"], 36 | }, 37 | "-p/2": { 38 | "in": ["-10944121435919637611123202872628637544274182200208017171849102093287904247808", "10944121435919637611123202872628637544274182200208017171849102093287904247809"], 39 | }, 40 | }; 41 | 42 | 43 | describe("WASM: Eq", function () { 44 | let circuit; 45 | this.timeout(100000); 46 | 47 | before(async function() { 48 | circuit = await wasm_tester(path.join(__dirname, "./circuits/", "eq.circom")); 49 | }); 50 | 51 | for (const test_name in test_vectors) { 52 | it(test_name, async() => { 53 | let w = await circuit.calculateWitness({ "in": test_vectors[test_name].in }, true); 54 | const expOut = {out: 1} 55 | 56 | await circuit.assertOut(w, expOut); 57 | await circuit.checkConstraints(w); 58 | }); 59 | } 60 | }); 61 | 62 | describe.skip("C: Eq", function () { 63 | let circuit; 64 | this.timeout(100000); 65 | 66 | before(async function() { 67 | circuit = await c_tester(path.join(__dirname, "./circuits/", "eq.circom")) 68 | }); 69 | 70 | for (const test_name in test_vectors) { 71 | it(test_name, async() => { 72 | let w = await circuit.calculateWitness({ "in": test_vectors[test_name].in }, true); 73 | const expOut = {out: 1} 74 | 75 | await circuit.assertOut(w, expOut); 76 | await circuit.checkConstraints(w); 77 | }); 78 | } 79 | }); 80 | -------------------------------------------------------------------------------- /test/offchain/credentialAtomicQueryV3OffChain.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from "chai"; 2 | import {describe} from "mocha"; 3 | 4 | const path = require("path"); 5 | const wasmTester = require("circom_tester").wasm; 6 | 7 | describe("Test credentialAtomicQueryV3OffChain.circom", function () { 8 | 9 | this.timeout(600000); 10 | 11 | let circuit; 12 | 13 | before(async () => { 14 | circuit = await wasmTester( 15 | path.join(__dirname, "../../circuits", "credentialAtomicQueryV3.circom"), 16 | { 17 | output: path.join(__dirname, "circuits", "build"), 18 | recompile: true, 19 | }, 20 | ); 21 | 22 | }); 23 | 24 | after(async () => { 25 | circuit.release() 26 | }) 27 | 28 | const sigBasePath = '../../testvectorgen/credentials/v3/testdata/sig' 29 | const mtpBasePath = '../../testvectorgen/credentials/v3/testdata/mtp' 30 | const tests = [ 31 | // sig 32 | require(`${sigBasePath}/claimIssuedOnProfileID.json`), 33 | require(`${sigBasePath}/claimIssuedOnProfileID2.json`), 34 | require(`${sigBasePath}/claimIssuedOnUserID.json`), 35 | require(`${sigBasePath}/profileID_subject.json`), 36 | require(`${sigBasePath}/claimNonMerklized.json`), 37 | require(`${sigBasePath}/claimWithLinkNonce.json`), 38 | require(`${sigBasePath}/between_operator.json`), 39 | require(`${sigBasePath}/less_than_eq_operator.json`), 40 | require(`${sigBasePath}/selective_disclosure.json`), 41 | require(`${sigBasePath}/nullify.json`), 42 | require(`${sigBasePath}/revoked_claim_without_revocation_check.json`), 43 | require(`${sigBasePath}/jsonld_non_inclusion.json`), 44 | require(`${sigBasePath}/noop_operator.json`), 45 | require(`${sigBasePath}/not_between_operator.json`), 46 | require(`${sigBasePath}/in_operator.json`), 47 | 48 | // mtp 49 | require(`${mtpBasePath}/claimIssuedOnProfileID.json`), 50 | require(`${mtpBasePath}/claimIssuedOnProfileID2.json`), 51 | require(`${mtpBasePath}/claimIssuedOnUserID.json`), 52 | require(`${mtpBasePath}/profileID_subject.json`), 53 | require(`${mtpBasePath}/claimNonMerklized.json`), 54 | require(`${mtpBasePath}/claimWithLinkNonce.json`), 55 | require(`${mtpBasePath}/between_operator.json`), 56 | require(`${mtpBasePath}/less_than_eq_operator.json`), 57 | require(`${mtpBasePath}/selective_disclosure.json`), 58 | require(`${mtpBasePath}/nullify.json`), 59 | require(`${mtpBasePath}/revoked_claim_without_revocation_check.json`), 60 | require(`${mtpBasePath}/noop_operator.json`), 61 | require(`${mtpBasePath}/not_between_operator.json`), 62 | require(`${mtpBasePath}/in_operator.json`), 63 | ]; 64 | 65 | tests.forEach(({ desc, inputs, expOut }) => { 66 | it(`${desc}`, async function () { 67 | const w = await circuit.calculateWitness(inputs, true); 68 | await circuit.assertOut(w, expOut); 69 | await circuit.checkConstraints(w); 70 | }); 71 | }); 72 | 73 | const failTestCase = [ 74 | require(`${sigBasePath}/revoked_claim_with_revocation_check.json`), 75 | require(`${mtpBasePath}/revoked_claim_with_revocation_check.json`), 76 | ]; 77 | 78 | failTestCase.forEach(({ desc, inputs, expOut }) => { 79 | it(`${desc}`, async function () { 80 | let error; 81 | await circuit.calculateWitness(inputs, true).catch((err) => { 82 | error = err; 83 | }); 84 | expect(error.message).to.include("Error in template checkClaimNotRevoked"); 85 | }) 86 | }); 87 | 88 | const failInTestCase = [ 89 | require(`${sigBasePath}/in_operator_failed_0.json`), 90 | require(`${mtpBasePath}/in_operator_failed_0.json`), 91 | ]; 92 | 93 | failInTestCase.forEach(({ desc, inputs, expOut }) => { 94 | it(`${desc}`, async function () { 95 | let error; 96 | await circuit.calculateWitness(inputs, true).catch((err) => { 97 | error = err; 98 | }); 99 | expect(error.message).to.include("Error in template ProcessQueryWithModifiers"); 100 | }) 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /test/onchain/credentialAtomicQueryV3OnChain.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from "chai"; 2 | import {describe} from "mocha"; 3 | 4 | const path = require("path"); 5 | const wasmTester = require("circom_tester").wasm; 6 | 7 | describe("Test credentialAtomicQueryV3OnChain.circom", function () { 8 | 9 | this.timeout(600000); 10 | 11 | let circuit; 12 | 13 | before(async () => { 14 | circuit = await wasmTester( 15 | path.join(__dirname, "../../circuits", "credentialAtomicQueryV3OnChain.circom"), 16 | { 17 | output: path.join(__dirname, "circuits", "build"), 18 | recompile: true, 19 | }, 20 | ); 21 | 22 | }); 23 | 24 | after(async () => { 25 | circuit.release() 26 | }) 27 | 28 | const sigBasePath = '../../testvectorgen/credentials/onchain/v3/testdata/sig' 29 | const mtpBasePath = '../../testvectorgen/credentials/onchain/v3/testdata/mtp' 30 | const tests = [ 31 | 32 | // sig 33 | require(`${sigBasePath}/claimIssuedOnProfileID.json`), 34 | require(`${sigBasePath}/claimIssuedOnProfileID2.json`), 35 | require(`${sigBasePath}/claimIssuedOnUserID.json`), 36 | require(`${sigBasePath}/profileID_subject_userid.json`), 37 | require(`${sigBasePath}/claimNonMerklized.json`), 38 | require(`${sigBasePath}/claimWithLinkNonce.json`), 39 | require(`${sigBasePath}/between_operator.json`), 40 | require(`${sigBasePath}/less_than_eq_operator.json`), 41 | require(`${sigBasePath}/selective_disclosure.json`), 42 | require(`${sigBasePath}/nullify.json`), 43 | require(`${sigBasePath}/revoked_claim_without_revocation_check.json`), 44 | require(`${sigBasePath}/jsonld_non_inclusion.json`), 45 | require(`${sigBasePath}/noop_operator.json`), 46 | require(`${sigBasePath}/onchainIdentity.json`), 47 | 48 | // mtp 49 | require(`${mtpBasePath}/claimIssuedOnProfileID.json`), 50 | require(`${mtpBasePath}/claimIssuedOnProfileID2.json`), 51 | require(`${mtpBasePath}/claimIssuedOnUserID.json`), 52 | require(`${mtpBasePath}/profileID_subject_userid.json`), 53 | require(`${mtpBasePath}/claimNonMerklized.json`), 54 | require(`${mtpBasePath}/claimWithLinkNonce.json`), 55 | require(`${mtpBasePath}/between_operator.json`), 56 | require(`${mtpBasePath}/less_than_eq_operator.json`), 57 | require(`${mtpBasePath}/selective_disclosure.json`), 58 | require(`${mtpBasePath}/nullify.json`), 59 | require(`${mtpBasePath}/revoked_claim_without_revocation_check.json`), 60 | require(`${mtpBasePath}/noop_operator.json`), 61 | require(`${mtpBasePath}/onchainIdentity.json`), 62 | ]; 63 | 64 | tests.forEach(({ desc, inputs, expOut }) => { 65 | it(`${desc}`, async function () { 66 | const w = await circuit.calculateWitness(inputs, true); 67 | await circuit.assertOut(w, expOut); 68 | await circuit.checkConstraints(w); 69 | }); 70 | }); 71 | 72 | const failTestCase = [ 73 | require(`${sigBasePath}/revoked_claim_with_revocation_check.json`), 74 | require(`${mtpBasePath}/revoked_claim_with_revocation_check.json`), 75 | ] 76 | 77 | failTestCase.forEach(({ desc, inputs, expOut }) => { 78 | it(`${desc}`, async function () { 79 | let error; 80 | await circuit.calculateWitness(inputs, true).catch((err) => { 81 | error = err; 82 | }); 83 | expect(error.message).to.include("Error in template checkClaimNotRevoked"); 84 | }) 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /test/poseidon.test.ts: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const tester = require("circom_tester").wasm; 3 | const buildPoseidon = require("circomlibjs").buildPoseidonOpt; 4 | let poseidon; 5 | const chai = require("chai"); 6 | const assert = chai.assert; 7 | 8 | export {}; 9 | 10 | describe("poseidon test", function () { 11 | this.timeout(200000); 12 | 13 | before(async function() { 14 | poseidon = await buildPoseidon(); 15 | }); 16 | 17 | // before(async () => { 18 | // poseidon = await circomlibjs.buildPoseidon(); 19 | // }); 20 | // before(async () => { 21 | // globalThis.curve_bn128.terminate(); 22 | // }); 23 | 24 | it("Test circomlib/poseidon. 3 inputs", async () => { 25 | const circuit = await tester( 26 | path.join(__dirname, "circuits", "poseidon.circom"), 27 | ); 28 | 29 | let witness = await circuit.calculateWitness({ 30 | in: ["0", "0", "0"] 31 | }); 32 | await circuit.checkConstraints(witness); 33 | await circuit.assertOut(witness, {out: "5317387130258456662214331362918410991734007599705406860481038345552731150762"}); 34 | 35 | witness = await circuit.calculateWitness({ 36 | in: ["1", "0", "0"] 37 | }); 38 | await circuit.checkConstraints(witness); 39 | await circuit.assertOut(witness, {out: "16319005924338521988144249782199320915969277491928916027259324394544057385749"}); 40 | 41 | // check with different inputs 42 | witness = await circuit.calculateWitness({ 43 | in: ["2", "0", "0"] 44 | }); 45 | await circuit.checkConstraints(witness); 46 | await circuit.assertOut(witness, {out: "13234400070188801104792523922697988244748411503422448631147834118387475842488"}); 47 | 48 | const testValues = ["72057594037927936", "1", "20634138280259599560273310290025659992320584624461316485434108770067472477956"]; 49 | witness = await circuit.calculateWitness({ 50 | in: testValues 51 | }); 52 | await circuit.checkConstraints(witness); 53 | await circuit.assertOut(witness, {out: "3135714887432857880402997813814046724922969450336546007917491784497158924950"}); 54 | }); 55 | 56 | it("Test circomlibjs/poseidon. 3 inputs", async () => { 57 | // check circomlib javascript poseidon output 58 | let jsOut = poseidon([1, 0, 0]); 59 | assert(poseidon.F.eq(poseidon.F.e("16319005924338521988144249782199320915969277491928916027259324394544057385749"), jsOut)); 60 | 61 | // check circomlib javascript poseidon output 62 | const testValues = ["72057594037927936", "1", "20634138280259599560273310290025659992320584624461316485434108770067472477956"]; 63 | jsOut = poseidon(testValues); 64 | assert(poseidon.F.eq(poseidon.F.e("3135714887432857880402997813814046724922969450336546007917491784497158924950"), jsOut)); 65 | }); 66 | 67 | it("Test circomlib/poseidon. 14 inputs", async () => { 68 | 69 | // poseidon with 14 inputs 70 | const circuit = await tester( 71 | path.join(__dirname, "circuits", "poseidon14.circom"), 72 | ); 73 | 74 | let witness = await circuit.calculateWitness({ 75 | in: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14"] 76 | }); 77 | await circuit.checkConstraints(witness); 78 | await circuit.assertOut(witness, {out: "8354478399926161176778659061636406690034081872658507739535256090879947077494"}); 79 | 80 | // different inputs 81 | witness = await circuit.calculateWitness({ 82 | in: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "0", "0", "0", "0"] 83 | }); 84 | await circuit.checkConstraints(witness); 85 | await circuit.assertOut(witness, {out: "5540388656744764564518487011617040650780060800286365721923524861648744699539"}); 86 | 87 | }); 88 | 89 | it("Test circomlibjs/poseidon. 14 inputs", async () => { 90 | 91 | // check circomlib javascript poseidon output 92 | let jsOut = poseidon([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]); 93 | assert(poseidon.F.eq(poseidon.F.e("8354478399926161176778659061636406690034081872658507739535256090879947077494"), jsOut)); 94 | 95 | // check circomlib javascript poseidon output 96 | jsOut = poseidon([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0]); 97 | assert(poseidon.F.eq(poseidon.F.e("5540388656744764564518487011617040650780060800286365721923524861648744699539"), jsOut)); 98 | }); 99 | 100 | it("Test circomlib/poseidon. 16 inputs", async () => { 101 | 102 | // poseidon with 16 inputs 103 | const circuit = await tester( 104 | path.join(__dirname, "circuits", "poseidon16.circom") 105 | ); 106 | 107 | let witness = await circuit.calculateWitness({ 108 | in: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"] 109 | }); 110 | await circuit.checkConstraints(witness); 111 | await circuit.assertOut(witness, {out: "9989051620750914585850546081941653841776809718687451684622678807385399211877"}); 112 | 113 | witness = await circuit.calculateWitness({ 114 | in: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "0", "0", "0", "0", "0", "0"] 115 | }); 116 | await circuit.checkConstraints(witness); 117 | await circuit.assertOut(witness, {out: "11882816200654282475720830292386643970958445617880627439994635298904836126497"}); 118 | }); 119 | 120 | it("Test circomlibjs/poseidon. 16 inputs", async () => { 121 | 122 | // check circomlib javascript poseidon output 123 | let jsOut = poseidon([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); 124 | assert(poseidon.F.eq(poseidon.F.e("9989051620750914585850546081941653841776809718687451684622678807385399211877"), jsOut)); 125 | 126 | // check circomlib javascript poseidon output 127 | jsOut = poseidon([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0]); 128 | assert(poseidon.F.eq(poseidon.F.e("11882816200654282475720830292386643970958445617880627439994635298904836126497"), jsOut)); 129 | }); 130 | }); 131 | 132 | -------------------------------------------------------------------------------- /test/query/in.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe("Test IN operator:", async function() { 7 | const tests = [ 8 | {desc: "value in the list", 9 | input: { 10 | in: "123", 11 | value: ["123", "9999", "5555"], 12 | }, 13 | expOut: {out: "1"}}, 14 | 15 | {desc: "multiple values in the list", 16 | input: { 17 | in: "123", 18 | value: ["123", "123", "999"], 19 | }, 20 | expOut: {out: "1"}}, 21 | {desc: "value not in the list", 22 | input: { 23 | in: "123", 24 | value: ["124", "888", "999"], 25 | }, 26 | expOut: {out: "0"}}, 27 | 28 | ]; 29 | 30 | let circuit; 31 | 32 | before(async function() { 33 | circuit = await wasm_tester(path.join(__dirname, "../circuits/query/", "inTest.circom")); 34 | }); 35 | 36 | tests.forEach(({desc, input, expOut}) => { 37 | it(`${desc}`, async function() { 38 | const w = await circuit.calculateWitness(input, true); 39 | await circuit.assertOut(w, expOut); 40 | await circuit.checkConstraints(w); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/stateTransition.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasmTester = require("circom_tester").wasm; 5 | 6 | // export {}; 7 | 8 | describe("Test stateTransition.circom", function() { 9 | 10 | this.timeout(600000); 11 | 12 | let circuit; 13 | 14 | before(async () => { 15 | circuit = await wasmTester( 16 | path.join(__dirname, "../circuits/", "stateTransition.circom"), 17 | { 18 | output: path.join(__dirname, "circuits", "build"), 19 | recompile: true, 20 | }, 21 | ); 22 | 23 | }); 24 | 25 | after(async () => { 26 | circuit.release() 27 | }) 28 | 29 | const basePath = '../testvectorgen/statetransition/testdata' 30 | const tests = [ 31 | require(`${basePath}/genesis_state.json`), 32 | require(`${basePath}/not_genesis_state.json`), 33 | ]; 34 | 35 | tests.forEach(({desc, inputs, expOut}) => { 36 | it(`${desc}`, async function() { 37 | const w = await circuit.calculateWitness(inputs, true); 38 | await circuit.assertOut(w, expOut); 39 | await circuit.checkConstraints(w); 40 | }); 41 | }); 42 | 43 | }); 44 | -------------------------------------------------------------------------------- /test/utils/arraySizeValidator.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe("Test ArraySizeValidator template:", async function () { 7 | const tests = [ 8 | { 9 | desc: "eq require 1 value array size input", 10 | input: { 11 | valueArraySize: "1", 12 | operator: "1", 13 | }, 14 | expOut: { out: "1" } 15 | }, 16 | { 17 | desc: "between require 2 value array size input", 18 | input: { 19 | valueArraySize: "2", 20 | operator: "9", 21 | }, 22 | expOut: { out: "1" } 23 | }, 24 | { 25 | desc: "in require less than 64 size input", 26 | input: { 27 | valueArraySize: "64", 28 | operator: "4", 29 | }, 30 | expOut: { out: "1" } 31 | }, 32 | { 33 | desc: "nin more than 64 size input", 34 | input: { 35 | valueArraySize: "65", 36 | operator: "5", 37 | }, 38 | expOut: { out: "0" } 39 | }, 40 | { 41 | desc: "sd with 1 value arr size", 42 | input: { 43 | valueArraySize: "1", 44 | operator: "16", 45 | }, 46 | expOut: { out: "0" } 47 | }, 48 | { 49 | desc: "sd with 0 value arr size", 50 | input: { 51 | valueArraySize: "0", 52 | operator: "16", 53 | }, 54 | expOut: { out: "1" } 55 | }, 56 | { 57 | desc: "gte with 2 value arr size", 58 | input: { 59 | valueArraySize: "2", 60 | operator: "8", 61 | }, 62 | expOut: { out: "0" } 63 | }, 64 | { 65 | desc: "nin eq 0", 66 | input: { 67 | valueArraySize: "0", 68 | operator: "5", 69 | }, 70 | expOut: { out: "0" } 71 | }, 72 | { 73 | desc: "in eq 0", 74 | input: { 75 | valueArraySize: "0", 76 | operator: "4", 77 | }, 78 | expOut: { out: "0" } 79 | }, 80 | { 81 | desc: "in eq max", 82 | input: { 83 | valueArraySize: "64", 84 | operator: "4", 85 | }, 86 | expOut: { out: "1" } 87 | }, 88 | { 89 | desc: "in more than max", 90 | input: { 91 | valueArraySize: "66", 92 | operator: "4", 93 | }, 94 | expOut: { out: "0" } 95 | }, 96 | ]; 97 | 98 | let circuit; 99 | 100 | before(async function () { 101 | circuit = await wasm_tester(path.join(__dirname, "../circuits/utils/", "utils_arraySizeValidatorTest.circom")); 102 | }); 103 | 104 | tests.forEach(({ desc, input, expOut }) => { 105 | it(`${desc}`, async function () { 106 | const w = await circuit.calculateWitness(input, true); 107 | await circuit.assertOut(w, expOut); 108 | await circuit.checkConstraints(w); 109 | }); 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /test/utils/claimUtils_getClaimHeader.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe("Test getClaimHeader:", async function() { 7 | const tests = [ 8 | { 9 | desc: "Test claim header", 10 | inputs: {claim: ["10889035741470030830827987437816582766592", "0", "999", "0", "0", "0", "0", "0"]}, 11 | expOut: {schema: 0, claimFlags: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}, 12 | }, 13 | { 14 | desc: "Test claim header", 15 | inputs: {claim: ["21778071482940061661655974875633165533184", "0", "999", "888", "0", "0", "777", "666"]}, 16 | expOut: {schema: 0, claimFlags: [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}, 17 | }, 18 | ]; 19 | 20 | let circuit; 21 | 22 | before(async function() { 23 | circuit = await wasm_tester(path.join(__dirname, "../circuits/utils", "claimUtils_getClaimHeader.circom")); 24 | }); 25 | 26 | tests.forEach(({desc, inputs, expOut}) => { 27 | it(`getClaimHeader ${desc}`, async function() { 28 | const w = await circuit.calculateWitness(inputs, true); 29 | await circuit.assertOut(w, expOut); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/utils/claimUtils_getClaimMerklizeRoot.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe("Test getClaimMerklizeRoot:", async function() { 7 | const tests = [ 8 | { 9 | desc: "Test claim merklize flag", 10 | inputs: { 11 | claim: ["10889035741470030830827987437816582766592", "0", "999", "0", "0", "0", "0", "0"], 12 | claimFlags: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 13 | }, 14 | expOut: {out: 999, flag: 1}, 15 | }, 16 | { 17 | desc: "Test claim merklize flag", 18 | inputs: { 19 | claim: ["21778071482940061661655974875633165533184", "0", "999", "888", "0", "0", "777", "666"], 20 | claimFlags: [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 21 | }, 22 | expOut: {out: 777, flag: 1}, 23 | }, 24 | ]; 25 | 26 | let circuit; 27 | 28 | before(async function() { 29 | circuit = await wasm_tester(path.join(__dirname, "../circuits/utils", "claimUtils_getClaimMerklizeRoot.circom")); 30 | }); 31 | 32 | tests.forEach(({desc, inputs, expOut}) => { 33 | it(`getClaimMerklizeFlag ${desc}`, async function() { 34 | const w = await circuit.calculateWitness(inputs, true); 35 | await circuit.assertOut(w, expOut); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/utils/getClaimExpiration.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe('Test getClaimExpiration:', async function() { 7 | const tests = [ 8 | {desc:'success', 9 | input: {claim:["2722258935367507707706996859454145691697", 10 | "0", 11 | "0", 12 | "0", 13 | "30803922974473213664682835967", // expiration 1669884010 14 | "0", 15 | "0", 16 | "0"],}, 17 | expOut: {expiration:1669884010}}, 18 | 19 | { 20 | desc: 'expiration 0 revocation 0', 21 | input: {claim:["2722258935367507707706996859454145691697", 22 | "0", 23 | "0", 24 | "0", 25 | "0", // revocation 0 && expiration 0 26 | "0", 27 | "0", 28 | "0"]}, 29 | expOut: {expiration: 0} 30 | }, 31 | 32 | { 33 | desc: "expiration max revocation 0", 34 | input: { 35 | claim: ["2722258935367507707706996859454145691697", 36 | "0", 37 | "0", 38 | "0", 39 | "170141183460469231713240559642174554112", // revocation 0 && expiration MAX 40 | "0", 41 | "0", 42 | "0"] 43 | }, 44 | expOut: {expiration: "9223372036854775807"} 45 | }, 46 | 47 | { 48 | desc: 'expiration max & revocation max', 49 | input: { 50 | claim: ["2722258935367507707706996859454145691697", 51 | "0", 52 | "0", 53 | "0", 54 | "170141183460469231731687303715884105727", // revocation MAX && expiration MAX 55 | "0", 56 | "0", 57 | "0"] 58 | }, 59 | expOut: {expiration: "9223372036854775807"} 60 | } 61 | 62 | 63 | ]; 64 | 65 | let circuit; 66 | 67 | before(async function() { 68 | circuit = await wasm_tester(path.join(__dirname, "../circuits/utils", "utils_getClaimExpiration.circom")); 69 | }); 70 | 71 | tests.forEach(({desc, input,expOut}) => { 72 | it(`getClaimExpiration ${desc}`, async function() { 73 | const w = await circuit.calculateWitness(input, true); 74 | await circuit.assertOut(w, expOut); 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /test/utils/getClaimSubjectOtherIden.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe("Test", function () { 7 | let circuit; 8 | let testData = {in:{},expOut:{}} 9 | 10 | before(async function() { 11 | circuit = await wasm_tester(path.join(__dirname, "../circuits/utils", "utils_getClaimSubjectOtherIden.circom")); 12 | }) 13 | 14 | afterEach( async ()=>{ 15 | const w = await circuit.calculateWitness(testData.in, true); 16 | await circuit.assertOut(w, testData.expOut); 17 | }) 18 | 19 | it("#self subject", async () => { 20 | // generated with: claim, err := NewClaim(schemaHash) 21 | const claim = ["0", "0", "0", "0", "0", "0", "0", "0"] 22 | 23 | testData.in = {claim: claim}; 24 | testData.expOut = {id: "0"}; 25 | }) 26 | 27 | it("#subject position index", async () => { 28 | // generated with 29 | /* 30 | id, err := IDFromString("114vgnnCupQMX4wqUBjg5kUya3zMXfPmKc9HNH4TSE") 31 | claim, err := NewClaim(schemaHash, WithID(id, IDPositionIndex)) 32 | */ 33 | const claim = [ 34 | "680564733841876926926749214863536422912", 35 | "436519927146362718106026092069337374589932286960467750019485473174216638464", 36 | "0", 37 | "0", 38 | "0", 39 | "0", 40 | "0", 41 | "0", 42 | ]; 43 | 44 | testData.in = {claim: claim}; 45 | testData.expOut = {id: "436519927146362718106026092069337374589932286960467750019485473174216638464"}; 46 | }); 47 | 48 | it("#subject position value", async () => { 49 | // generated with 50 | /* 51 | id, err := IDFromString("114vgnnCupQMX4wqUBjg5kUya3zMXfPmKc9HNH4TSE") 52 | claim, err := NewClaim(schemaHash, WithID(id, IDPositionValue)) 53 | */ 54 | const claim = [ 55 | "1020847100762815390390123822295304634368", 56 | "0", 57 | "0", 58 | "0", 59 | "0", 60 | "436519927146362718106026092069337374589932286960467750019485473174216638464", 61 | "0", 62 | "0", 63 | ]; 64 | 65 | testData.in = {claim: claim}; 66 | testData.expOut = {id: "436519927146362718106026092069337374589932286960467750019485473174216638464"}; 67 | }); 68 | }) 69 | -------------------------------------------------------------------------------- /test/utils/getSubjectLocation.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe("Test", function () { 7 | let circuit; 8 | let testData = {in:{},expOut:{}} 9 | 10 | before(async function() { 11 | circuit = await wasm_tester(path.join(__dirname, "../circuits/utils", "utils_getSubjectLocation.circom")); 12 | }) 13 | 14 | afterEach( async ()=>{ 15 | const w = await circuit.calculateWitness(testData.in, true); 16 | await circuit.assertOut(w, testData.expOut); 17 | }) 18 | 19 | it("#self location", async () => { 20 | // this claim generated with claim, err := NewClaim(schemaHash) 21 | const claim = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 22 | 23 | testData.in = {claimFlags: claim}; 24 | testData.expOut = {out: 0}; 25 | }); 26 | 27 | it("#index location", async () => { 28 | // this claim generated with claim, err := NewClaim(schemaHash, WithID(ID{}, IDPositionIndex)) 29 | const claim = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 30 | 31 | testData.in = {claimFlags: claim}; 32 | testData.expOut = {out: 2}; 33 | }); 34 | 35 | it("#value location", async () => { 36 | // this claim generated with `claim, err := NewClaim(schemaHash, WithID(ID{}, IDPositionValue))` 37 | const claim = [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 38 | 39 | testData.in = {claimFlags: claim}; 40 | testData.expOut = {out: 3}; 41 | }); 42 | }) 43 | -------------------------------------------------------------------------------- /test/utils/getValueByIndex.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const tester = require("circom_tester").wasm; 5 | const chai = require("chai"); 6 | const assert = chai.assert; 7 | 8 | export {}; 9 | 10 | const Q = "21888242871839275222246405745257275088548364400416034343698204186575808495616"; 11 | 12 | describe("utils#getValueByIndex", function() { 13 | let circuit; 14 | let testData = {in:{},expOut:{}}; 15 | const claim =["0", 16 | "1", 17 | "2", 18 | "3", 19 | "4", 20 | "5", 21 | Q, 22 | "7"]; 23 | 24 | before(async function() { 25 | circuit = await tester( 26 | path.join(__dirname, "../circuits/utils", "utils_GetValueByIndex.circom"), 27 | ); 28 | }); 29 | 30 | afterEach( async ()=>{ 31 | const w = await circuit.calculateWitness(testData.in, true); 32 | await circuit.assertOut(w, testData.expOut); 33 | circuit.release(); 34 | }) 35 | 36 | it("#Get slot index 0", async () => { 37 | testData.in = {claim: claim, index: "0"}; 38 | testData.expOut = {value: 0}; 39 | }); 40 | 41 | it("#Get slot index 1", async () => { 42 | testData.in = {claim: claim, index: "1"}; 43 | testData.expOut = {value: 1}; 44 | }); 45 | 46 | it("#Get slot index 2", async () => { 47 | testData.in = {claim: claim, index: "2"}; 48 | testData.expOut = {value: 2}; 49 | }); 50 | 51 | it("#Get slot index 7 max field value", async () => { 52 | testData.in = {claim: claim, index: "6"}; 53 | testData.expOut = {value: Q}; 54 | }); 55 | 56 | it("#Get slot index 8", async () => { 57 | testData.in = {claim: claim, index: "7"}; 58 | testData.expOut = {value: 7}; 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/utils/idUtils_CalculateIdChecksum.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | // inputs MUST be generated by GO-CIRCUITS library https://github.com/iden3/go-circuits (using corresponding test) 7 | describe("idUtils.circom:", async function() { 8 | 9 | const tests = [ 10 | { 11 | desc: "First", 12 | input: { 13 | typ: "49648", 14 | genesis: "12590477270745565760216918871818154904274440992307045641577748017", 15 | }, 16 | output: { 17 | out: "4565", 18 | }, 19 | }, 20 | { 21 | desc: "Max values", 22 | input: { 23 | typ: "65535", // 0xffff 24 | // 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff 25 | genesis: "105312291668557186697918027683670432318895095400549111254310977535", 26 | }, 27 | output: { 28 | out: "7395", // 0x1CE3 - max checksum value possible 29 | }, 30 | }, 31 | ]; 32 | 33 | let circuit; 34 | this.timeout(300000) 35 | 36 | before(async () => { 37 | circuit = await wasm_tester( 38 | path.join(__dirname, "../circuits", "idUtils_CalculateIdChecksum.circom"), 39 | { 40 | output: path.join(__dirname, "../circuits", "build/idUtils_CalculateIdChecksum"), 41 | recompile: true, 42 | }, 43 | ); 44 | }); 45 | 46 | tests.forEach(({desc, input, output}) => { 47 | it(`CalculateIdChecksum - ${desc}`, async function() { 48 | const w = await circuit.calculateWitness(input, true); 49 | await circuit.checkConstraints(w); 50 | await circuit.assertOut(w, output); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/utils/idUtils_GatherID.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | // inputs MUST be generated by GO-CIRCUITS library https://github.com/iden3/go-circuits (using corresponding test) 7 | describe("idUtils.circom:", async function() { 8 | 9 | const tests = [ 10 | { 11 | desc: "First", 12 | input: { 13 | typ: "49648", 14 | genesis: "12590477270745565760216918871818154904274440992307045641577748017", 15 | checksum: "4565", 16 | }, 17 | output: { 18 | out: "31507297202617339271037322087289804665039118987722686458860089520937878000", 19 | }, 20 | }, 21 | ]; 22 | 23 | let circuit; 24 | this.timeout(300000) 25 | 26 | before(async () => { 27 | circuit = await wasm_tester( 28 | path.join(__dirname, "../circuits", "idUtils_GatherID.circom"), 29 | { 30 | output: path.join(__dirname, "../circuits", "build/idUtils_GatherID"), 31 | recompile: true, 32 | }, 33 | ); 34 | }); 35 | 36 | tests.forEach(({desc, input, output}) => { 37 | it(`GatherID - ${desc}`, async function() { 38 | const w = await circuit.calculateWitness(input, true); 39 | await circuit.checkConstraints(w); 40 | await circuit.assertOut(w, output); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/utils/idUtils_NewID.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | // inputs MUST be generated by GO-CIRCUITS library https://github.com/iden3/go-circuits (using corresponding test) 7 | describe("idUtils.circom:", async function() { 8 | 9 | const tests = [ 10 | { 11 | desc: "First", 12 | input: { 13 | typ: "49648", 14 | genesis: "12590477270745565760216918871818154904274440992307045641577748017", 15 | }, 16 | output: { 17 | out: "31507297202617339271037322087289804665039118987722686458860089520937878000", 18 | }, 19 | }, 20 | ]; 21 | 22 | let circuit; 23 | this.timeout(300000) 24 | 25 | before(async () => { 26 | circuit = await wasm_tester( 27 | path.join(__dirname, "../circuits", "idUtils_NewID.circom"), 28 | { 29 | output: path.join(__dirname, "../circuits", "build/idUtils_NewID"), 30 | recompile: true, 31 | }, 32 | ); 33 | }); 34 | 35 | tests.forEach(({desc, input, output}) => { 36 | it(`NewID - ${desc}`, async function() { 37 | const w = await circuit.calculateWitness(input, true); 38 | await circuit.checkConstraints(w); 39 | await circuit.assertOut(w, output); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/utils/idUtils_ProfileID.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | // inputs MUST be generated by GO-CIRCUITS library https://github.com/iden3/go-circuits (using corresponding test) 7 | describe("idUtils.circom:", async function() { 8 | 9 | const tests = [ 10 | { 11 | desc: "Salted hash", 12 | input: { 13 | in: "23630567111950550539435915649280822148510307443797111728722609533581131776", //379949150130214723420589610911161895495647789006649785264738141299135414272 14 | nonce: "10", 15 | }, 16 | output: { 17 | out: "25425363284463910957419549722021124450832239517990785975889689633068548096", 18 | }, 19 | }, 20 | ]; 21 | 22 | let circuit; 23 | this.timeout(300000) 24 | 25 | before(async () => { 26 | circuit = await wasm_tester( 27 | path.join(__dirname, "../circuits", "idUtils_ProfileID.circom"), 28 | { 29 | output: path.join(__dirname, "../circuits", "build/idUtils_ProfileID"), 30 | recompile: true, 31 | }, 32 | ); 33 | }); 34 | 35 | tests.forEach(({desc, input, output}) => { 36 | it(`ProfileID - ${desc}`, async function() { 37 | const w = await circuit.calculateWitness(input, true); 38 | await circuit.checkConstraints(w); 39 | await circuit.assertOut(w, output); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/utils/idUtils_SelectProfile.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | // inputs MUST be generated by GO-CIRCUITS library https://github.com/iden3/go-circuits (using corresponding test) 7 | describe("idUtils_SelectProfile.circom:", async function() { 8 | 9 | const tests = [ 10 | { 11 | desc: "Salted hash", 12 | input: { 13 | in: "23630567111950550539435915649280822148510307443797111728722609533581131776", //379949150130214723420589610911161895495647789006649785264738141299135414272 14 | nonce: "10", 15 | }, 16 | output: { 17 | out: "25425363284463910957419549722021124450832239517990785975889689633068548096", 18 | }, 19 | }, 20 | ]; 21 | 22 | let circuit; 23 | this.timeout(300000) 24 | 25 | before(async () => { 26 | circuit = await wasm_tester( 27 | path.join(__dirname, "../circuits", "idUtils_SelectProfile.circom"), 28 | { 29 | output: path.join(__dirname, "../circuits", "build/idUtils_ProfileID"), 30 | recompile: true, 31 | }, 32 | ); 33 | }); 34 | 35 | tests.forEach(({desc, input, output}) => { 36 | it(`SelectProfile - ${desc}`, async function() { 37 | const w = await circuit.calculateWitness(input, true); 38 | await circuit.checkConstraints(w); 39 | await circuit.assertOut(w, output); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/utils/idUtils_SplitID.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | // inputs MUST be generated by GO-CIRCUITS library https://github.com/iden3/go-circuits (using corresponding test) 7 | describe("idUtils.circom:", async function() { 8 | 9 | const tests = [ 10 | { 11 | desc: "First", 12 | input: { 13 | id: "23630567111950550539435915649280822148510307443797111728722609533581131776", 14 | }, 15 | output: { 16 | typ: "0", 17 | genesis: "89864607032023545024815867714905163118918335844094091816545341738", 18 | checksum: "3423", 19 | }, 20 | }, 21 | ]; 22 | 23 | let circuit; 24 | this.timeout(300000) 25 | 26 | before(async () => { 27 | circuit = await wasm_tester( 28 | path.join(__dirname, "../circuits", "idUtils_SplitID.circom"), 29 | { 30 | output: path.join(__dirname, "../circuits", "build/idUtils_SplitID"), 31 | recompile: true, 32 | }, 33 | ); 34 | }); 35 | 36 | tests.forEach(({desc, input, output}) => { 37 | it(`SplitID - ${desc}`, async function() { 38 | const w = await circuit.calculateWitness(input, true); 39 | await circuit.checkConstraints(w); 40 | await circuit.assertOut(w, output); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/utils/idUtils_TakeNBits.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | // inputs MUST be generated by GO-CIRCUITS library https://github.com/iden3/go-circuits (using corresponding test) 7 | describe("idUtils.circom:", async function() { 8 | 9 | const tests = [ 10 | { 11 | desc: "First", 12 | input: { 13 | in: "13843376158434186874150155371608112991467708075683980446964675987616037973262", 14 | }, 15 | output: { 16 | out: "423621611607500778482958", 17 | }, 18 | }, 19 | ]; 20 | 21 | let circuit; 22 | this.timeout(300000) 23 | 24 | before(async () => { 25 | circuit = await wasm_tester( 26 | path.join(__dirname, "../circuits", "idUtils_TakeNBits.circom"), 27 | { 28 | output: path.join(__dirname, "../circuits", "build/idUtils_TakeNBits"), 29 | recompile: true, 30 | }, 31 | ); 32 | }); 33 | 34 | tests.forEach(({desc, input, output}) => { 35 | it(`TakeNBits - ${desc}`, async function() { 36 | const w = await circuit.calculateWitness(input, true); 37 | await circuit.checkConstraints(w); 38 | await circuit.assertOut(w, output); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/utils/isExpirable.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe("Test", function () { 7 | let circuit; 8 | let testData = {in:{},expOut:{}} 9 | 10 | before(async function() { 11 | circuit = await wasm_tester(path.join(__dirname, "../circuits/utils", "utils_isExpirable.circom")); 12 | }) 13 | 14 | afterEach( async ()=>{ 15 | const w = await circuit.calculateWitness(testData.in, true); 16 | await circuit.assertOut(w, testData.expOut); 17 | }) 18 | 19 | it("#not expirable", async () => { 20 | const claim = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 21 | 22 | testData.in = {claimFlags: claim}; 23 | testData.expOut = {out: 0}; 24 | }); 25 | 26 | it("#expirable", async () => { 27 | const claim = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 28 | 29 | testData.in = {claimFlags: claim}; 30 | testData.expOut = {out: 1}; 31 | }); 32 | }) 33 | -------------------------------------------------------------------------------- /test/utils/isUpdatable.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe("Test", function () { 7 | let circuit; 8 | let testData = {in:{},expOut:{}} 9 | 10 | before(async function() { 11 | circuit = await wasm_tester(path.join(__dirname, "../circuits/utils", "utils_isUpdatable.circom")); 12 | }) 13 | 14 | afterEach( async ()=>{ 15 | const w = await circuit.calculateWitness(testData.in, true); 16 | await circuit.assertOut(w, testData.expOut); 17 | }) 18 | 19 | it("#not updatable", async () => { 20 | const claim = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 21 | 22 | testData.in = {claimFlags: claim}; 23 | testData.expOut = {out: 0}; 24 | }); 25 | 26 | it("#updatable", async () => { 27 | const claim = [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 28 | 29 | testData.in = {claimFlags: claim}; 30 | testData.expOut = {out: 1}; 31 | }); 32 | }) 33 | -------------------------------------------------------------------------------- /test/utils/nullify.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe("Test Nullify template:", async function () { 7 | const tests = [ 8 | { 9 | desc: "nullify with all inputs non zero", 10 | input: { 11 | genesisID: "23148936466334350744548790012294489365207440754509988986684797708370051073", 12 | claimSubjectProfileNonce: "999", 13 | claimSchema: "180410020913331409885634153623124536270", 14 | verifierID: "21929109382993718606847853573861987353620810345503358891473103689157378049", 15 | nullifierSessionID: "94313", 16 | }, 17 | expOut: { nullifier: "1774255757463994926045333540514329781531189541970510727873068125458049917662" } 18 | }, 19 | { 20 | desc: "nullify with nullifierSessionID = zero", 21 | input: { 22 | genesisID: "23148936466334350744548790012294489365207440754509988986684797708370051073", 23 | claimSubjectProfileNonce: "999", 24 | claimSchema: "180410020913331409885634153623124536270", 25 | verifierID: "21929109382993718606847853573861987353620810345503358891473103689157378049", 26 | nullifierSessionID: "0", 27 | }, 28 | expOut: { nullifier: "0" } 29 | }, 30 | { 31 | desc: "nullify with credProfileNonce = zero", 32 | input: { 33 | genesisID: "23148936466334350744548790012294489365207440754509988986684797708370051073", 34 | claimSubjectProfileNonce: "0", 35 | claimSchema: "180410020913331409885634153623124536270", 36 | verifierID: "21929109382993718606847853573861987353620810345503358891473103689157378049", 37 | nullifierSessionID: "94313", 38 | }, 39 | expOut: { nullifier: "0" } 40 | }, 41 | { 42 | desc: "nullify with verifierID = zero", 43 | input: { 44 | genesisID: "23148936466334350744548790012294489365207440754509988986684797708370051073", 45 | claimSubjectProfileNonce: "999", 46 | claimSchema: "180410020913331409885634153623124536270", 47 | verifierID: "0", 48 | nullifierSessionID: "94313", 49 | }, 50 | expOut: { nullifier: "0" } 51 | }, 52 | ]; 53 | 54 | let circuit; 55 | 56 | before(async function () { 57 | circuit = await wasm_tester(path.join(__dirname, "../circuits/query/", "nullifyTest.circom")); 58 | }); 59 | 60 | tests.forEach(({ desc, input, expOut }) => { 61 | it(`${desc}`, async function () { 62 | const w = await circuit.calculateWitness(input, true); 63 | await circuit.assertOut(w, expOut); 64 | await circuit.checkConstraints(w); 65 | }); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/utils/spongeHash.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import path from "path"; 3 | import { wasm } from "circom_tester"; 4 | 5 | const templateName = "SpongeHash"; 6 | const relativePath = "../circuits"; 7 | const generateTemplate = ( 8 | size: number, 9 | circuitTemplateName = "SpongeHash" 10 | ): void => { 11 | const template = ` 12 | pragma circom 2.1.1; 13 | include "../../circuits/lib/utils/spongeHash.circom"; 14 | template ${circuitTemplateName}Test() { 15 | signal input in[{{n}}]; 16 | signal output out; 17 | component h = ${circuitTemplateName}({{n}},6); 18 | for(var i = 0; i < {{n}}; i++) { 19 | h.in[i] <== in[i]; 20 | } 21 | out <== h.out; 22 | } 23 | component main = ${circuitTemplateName}Test(); 24 | `; 25 | 26 | const circuitName = `${circuitTemplateName}${size}.circom`; 27 | if (!fs.existsSync(path.join(__dirname, relativePath, circuitName))) { 28 | fs.writeFileSync( 29 | path.join(__dirname, relativePath, circuitName), 30 | template.replace(/{{n}}/g, size.toString()) 31 | ); 32 | } 33 | }; 34 | 35 | describe("Sponge Hash tests", function () { 36 | this.timeout(400000); 37 | it("Test SpongeHash util using hash for different size inputs", async () => { 38 | const testCases = [ 39 | new Array(64).fill(0), 40 | new Array(63).fill(0).map((_, i) => i + 1), 41 | new Array(60).fill(0).map((_, i) => 60 - i), 42 | new Array(5).fill(0).map((_, i) => i + 1), 43 | [0], 44 | new Array(6).fill(0).map((_, i) => i + 1), 45 | new Array(16).fill(0).map((_, i) => i + 1), 46 | ]; 47 | 48 | const expected = [ 49 | "7368935780301629035733097554153370898490964345621267223639562510928947240459", 50 | "3027148895471770401984833121350831002277377476084832804937751928355120074994", 51 | "13254546416358473313457812414193018870743005197521155619424967381510427667259", 52 | "6186895146109816025093019628248576250523388957868658785525378722128520330607", 53 | "14408838593220040598588012778523101864903887657864399481915450526643617223637", 54 | "20400040500897583745843009878988256314335038853985262692600694741116813247201", 55 | "5605330091169856132381694679994923791994681609858984566508182442210285386845", 56 | ]; 57 | 58 | await createCircuit(testCases, expected); 59 | }); 60 | 61 | it("Compare SpongeHash with go-iden3-crypto", async () => { 62 | const expected = [ 63 | "7757418611592686851480213421395023492910069335464834810473637859830874759279", 64 | "15336558801450556532856248569924170992202208561737609669134139141992924267169", 65 | "1144067817111460038464347636467015864025755473684726783913963849059920017972", 66 | "17412321031092738336952455023828291176350572898965143678124844674611030278684", 67 | "6186895146109816025093019628248576250523388957868658785525378722128520330607", 68 | "20400040500897583745843009878988256314335038853985262692600694741116813247201", 69 | "5577102071379540901883430718956859114277228199376926918690006383418752242436", 70 | "1152305401687934645444055619201663931885907446826162025284948369145242973368", 71 | "8211686227523893359273736667704216885003209084307215502943363750368107369620", 72 | "7108881484930248270303049372327318360896856726757123411260066018366897025567", 73 | "2265321027947983264707184154085264659877433648022878713272356019112959947364", 74 | "12651359110916308876830620694657526370832930110397701742810260795463743022206", 75 | "5448696623590242880008365208951082811870613001921911478755586779573529615712", 76 | "12138957412533147284529235731676849096990688866708298976199544475739215311830", 77 | "4296304251107177078079123684673490646100950885652358062546507066452904816259", 78 | "5605330091169856132381694679994923791994681609858984566508182442210285386845", 79 | "13988934542900192765733497388343315006075364569889174469414974142779436870312", 80 | "15403024279602420951485303794282139684443426339931496210157338841814828581711", 81 | "21456291545549982243960095968662564139932500401819177068272967144559313156981", 82 | "18204869441381241555967353898895621136239168759533159329850061388567652528934", 83 | "13038015408593191211490686165474468640092531721798660195788216465453248480728", 84 | ]; 85 | 86 | const testCases = expected.map((_, i) => 87 | new Array(i + 1).fill(0).map((_, i) => i + 1) 88 | ); 89 | 90 | await createCircuit(testCases, expected); 91 | }); 92 | 93 | async function createCircuit(testCases: number[][], expected: string[]) { 94 | for (let index = 0; index < expected.length; index++) { 95 | generateTemplate(testCases[index].length); 96 | const circuit = await wasm( 97 | path.join( 98 | __dirname, 99 | relativePath, 100 | `${templateName}${testCases[index].length}.circom` 101 | ) 102 | ); 103 | 104 | const witness = await circuit.calculateWitness({ 105 | in: testCases[index], 106 | }); 107 | await circuit.checkConstraints(witness); 108 | await circuit.assertOut(witness, { 109 | out: expected[index], 110 | }); 111 | 112 | fs.unlinkSync( 113 | path.join( 114 | __dirname, 115 | relativePath, 116 | `${templateName}${testCases[index].length}.circom` 117 | ) 118 | ); 119 | } 120 | } 121 | }); 122 | -------------------------------------------------------------------------------- /test/utils/verifyExpirationTime.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const wasm_tester = require("circom_tester").wasm; 5 | 6 | describe("Test verifyExpirationTime", function() { 7 | let circuit; 8 | let testData = {in:{},expOut:{}}; 9 | 10 | before(async function() { 11 | this.timeout(5000); 12 | circuit = await wasm_tester(path.join(__dirname, "../circuits/utils", "utils_verifyExpirationTime.circom")); 13 | }); 14 | 15 | afterEach( async ()=>{ 16 | const w = await circuit.calculateWitness(testData.in, true); 17 | await circuit.assertOut(w, testData.expOut); 18 | }) 19 | 20 | it("#success claim with expiration time", async () => { 21 | const claim = ["8166776806102523123120990578362437074969", // claim type = 25, expiration flag 1 22 | "0", 23 | "0", 24 | "0", 25 | "30803922974473213664682835967", // expiration 1669884010 26 | "0", 27 | "0", 28 | "0"]; 29 | 30 | testData.in = {expirationFlag: "1", claim: claim, timestamp: 1669884009}; 31 | testData.expOut = {}; 32 | }); 33 | 34 | it("#success expiration flag 0", async () => { 35 | const claim = ["5444517870735015415413993718908291383321", // claim type = 25, expiration flag 0, expiration 0 36 | "0", 37 | "0", 38 | "0", 39 | "18446744073709551615", // expiration 0 40 | "0", 41 | "0", 42 | "0"]; 43 | 44 | testData.in = {expirationFlag: "0", claim: claim, timestamp: 1669884009}; 45 | testData.expOut = {}; 46 | }); 47 | 48 | it("#success expiration flag 1, timestamp equal to expiration", async () => { 49 | const claim = ["2722258935367507707706996859454145691673", // claim type = 25, expiration flag 1 50 | "0", 51 | "0", 52 | "0", 53 | "30802373438747650025492316159", // expiration 1669800009 54 | "0", 55 | "0", 56 | "0"]; 57 | 58 | testData.in = {expirationFlag: "1", claim: claim, timestamp: 1669800008}; 59 | testData.expOut = {}; 60 | }); 61 | 62 | }); 63 | -------------------------------------------------------------------------------- /test/utils/verifyFunctions.test.ts: -------------------------------------------------------------------------------- 1 | import {describe} from "mocha"; 2 | 3 | const path = require("path"); 4 | const tester = require("circom_tester").wasm; 5 | const chai = require("chai"); 6 | const assert = chai.assert; 7 | 8 | export {}; 9 | 10 | const verifyCredentialSubject = { 11 | enabled: "1", 12 | claim: [ 13 | "700576110560149417265602648140262015232", 14 | "197990912273762023075897629417744831667514652778362723486029975898079821824", 15 | "0", 16 | "0", 17 | "123", 18 | "0", 19 | "0", 20 | "0", 21 | ], 22 | id: "197990912273762023075897629417744831667514652778362723486029975898079821824", // 117twYCgGzxHUtMsAfjM3muCrypTXcu6oc7cSsuGHM 23 | } 24 | 25 | describe("utils checkIdenStateMatchesRoots test", function () { 26 | this.timeout(200000); 27 | it("Test utils checkIdenStateMatchesRoots", async () => { 28 | const circuit = await tester( 29 | path.join(__dirname, "../circuits/utils", "utils_checkIdenStateMatchesRoots.circom"), 30 | ); 31 | 32 | const witness = await circuit.calculateWitness({ 33 | "enabled": "1", 34 | "claimsTreeRoot": "5390978791160263927985161830452830346003784422812143177724675599288112176057", 35 | "revTreeRoot": "0", 36 | "rootsTreeRoot": "0", 37 | "expectedState": "17685575544241839934776615609352503109564813484662571173826983469932580732343" 38 | }, true); 39 | await circuit.checkConstraints(witness); 40 | }); 41 | }); 42 | 43 | describe("utils verifyClaimSignature test", function () { 44 | this.timeout(200000); 45 | it("Test utils verifyClaimSignature", async () => { 46 | const circuit = await tester( 47 | path.join(__dirname, "../circuits/utils", "utils_verifyClaimSignature.circom"), 48 | ); 49 | 50 | //"claim": ["0","0","0","0","0","0","0","0"] 51 | const witness = await circuit.calculateWitness({ 52 | "enabled": "1", 53 | "claimHash": "5723720832300544730179969191054372086051633243972178196193101286943139171509", 54 | "sigR8x": "9813265844413837380082826071463892301278045128546516139211810884421030840917", 55 | "sigR8y": "7110066446166689493462986682910785889642607369745074815971396692733663407188", 56 | "sigS": "1837652275043347007743363280039859735198580922853822340283578942174886737707", 57 | "pubKeyX": "11356572759147270709631238494624398626863089762419266085446886102966874017086", 58 | "pubKeyY": "6952793560627676182867513788009876275064024476317357446458237628508619978750" 59 | }, true); 60 | await circuit.checkConstraints(witness); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /testvectorgen/auth/authV3_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/hex" 6 | json2 "encoding/json" 7 | "fmt" 8 | "math/big" 9 | "testing" 10 | 11 | "test/utils" 12 | 13 | core "github.com/iden3/go-iden3-core/v2" 14 | "github.com/iden3/go-merkletree-sql/v2" 15 | "github.com/iden3/go-merkletree-sql/v2/db/memory" 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | const ( 20 | userPK = "28156abe7fe2fd433dc9df969286b96666489bac508612d0e16593e944c4f69e" 21 | userPK2 = "28156abe7fe2fd433dc9df969286b96666489bac508612d0e16593e944c4f69d" 22 | timestamp = "1642074362" 23 | ) 24 | 25 | type AuthV3Inputs struct { 26 | UserGenesisID string `json:"genesisID"` 27 | Nonce string `json:"profileNonce"` 28 | UserAuthClaim *core.Claim `json:"authClaim"` 29 | UserAuthClaimMtp []string `json:"authClaimIncMtp"` 30 | UserAuthClaimNonRevMtp []string `json:"authClaimNonRevMtp"` 31 | UserAuthClaimNonRevMtpAuxHi string `json:"authClaimNonRevMtpAuxHi"` 32 | UserAuthClaimNonRevMtpAuxHv string `json:"authClaimNonRevMtpAuxHv"` 33 | UserAuthClaimNonRevMtpNoAux string `json:"authClaimNonRevMtpNoAux"` 34 | Challenge string `json:"challenge"` 35 | ChallengeSignatureR8X string `json:"challengeSignatureR8x"` 36 | ChallengeSignatureR8Y string `json:"challengeSignatureR8y"` 37 | ChallengeSignatureS string `json:"challengeSignatureS"` 38 | UserClaimsTreeRoot string `json:"claimsTreeRoot"` 39 | UserRevTreeRoot string `json:"revTreeRoot"` 40 | UserRootsTreeRoot string `json:"rootsTreeRoot"` 41 | UserState string `json:"state"` 42 | GistRoot string `json:"gistRoot"` 43 | GistMtp []string `json:"gistMtp"` 44 | GistMtpAuxHi string `json:"gistMtpAuxHi"` 45 | GistMtpAuxHv string `json:"gistMtpAuxHv"` 46 | GistMtpNoAux string `json:"gistMtpNoAux"` 47 | } 48 | 49 | type AuthV3Outputs struct { 50 | ID string `json:"userID"` 51 | GistRoot string `json:"gistRoot"` 52 | Challenge string `json:"challenge"` 53 | } 54 | 55 | type TestDataAuthV3 struct { 56 | Desc string `json:"desc"` 57 | In AuthV3Inputs `json:"inputs"` 58 | Out AuthV3Outputs `json:"expOut"` 59 | } 60 | 61 | func Test_UserID_Subject(t *testing.T) { 62 | 63 | desc := "Ownership true. User state: genesis. Auth claims total/signedWith/revoked: 1/1/none" 64 | isUserIDProfile := false 65 | isSecondAuthClaim := false 66 | isUserStateGenesis := true 67 | 68 | generateAuthTestData(t, isUserIDProfile, isUserStateGenesis, isSecondAuthClaim, desc, "userID_genesis") 69 | } 70 | 71 | func TestNotGenesisUserSate(t *testing.T) { 72 | 73 | desc := "Ownership true. User state: not-genesis. Auth claims total/signedWith/revoked: 1/1/none" 74 | isUserIDProfile := false 75 | 76 | isUserStateGenesis := false 77 | isSecondAuthClaim := false 78 | 79 | generateAuthTestData(t, isUserIDProfile, isUserStateGenesis, isSecondAuthClaim, desc, "user_state_not_genesis") 80 | } 81 | 82 | func TestNotGenesisUserSateWithRevokedClaims(t *testing.T) { 83 | desc := "Ownership true. User state: not-genesis. Auth claims total/signedWith/revoked: 1/1/none" 84 | isUserIDProfile := false 85 | isUserStateGenesis := false 86 | isSecondAuthClaim := true 87 | generateAuthTestData(t, isUserIDProfile, isUserStateGenesis, isSecondAuthClaim, desc, 88 | "user_state_not_genesis_second_auth_claim") 89 | } 90 | 91 | func Test_ProfileID(t *testing.T) { 92 | 93 | desc := "nonce=10. ProfileID == UserID should be true. Ownership true. User state: genesis. Auth claims total/signedWith/revoked: 1/1/none" 94 | isUserIDProfile := true 95 | isSecondAuthClaim := false 96 | isUserStateGenesis := true 97 | 98 | generateAuthTestData(t, isUserIDProfile, isUserStateGenesis, isSecondAuthClaim, desc, "userID_profileID") 99 | } 100 | 101 | func generateAuthTestData(t *testing.T, profile, genesis, isSecondAuthClaim bool, desc, fileName string) { 102 | 103 | nonce := big.NewInt(0) 104 | 105 | challenge := big.NewInt(12345) 106 | 107 | user := utils.NewIdentity(t, userPK) 108 | 109 | var err error 110 | 111 | userProfile := user.ID 112 | if profile { 113 | nonce = big.NewInt(10) 114 | userProfile, err = core.ProfileID(user.ID, nonce) 115 | require.NoError(t, err) 116 | } 117 | 118 | gisTree, err := merkletree.NewMerkleTree(context.Background(), memory.NewMemoryStorage(), 40) 119 | require.Nil(t, err) 120 | gisTree.Add(context.Background(), big.NewInt(1), big.NewInt(1)) 121 | 122 | if genesis == false { 123 | // extract pubKey 124 | authClaim2, pk2 := utils.NewAuthClaim(t, userPK2) 125 | 126 | user.AddClaim(t, authClaim2) 127 | 128 | if isSecondAuthClaim { 129 | 130 | // revoke auth claim 131 | revNonce := user.AuthClaim.GetRevocationNonce() 132 | err = user.Ret.Add(context.Background(), new(big.Int).SetUint64(revNonce), big.NewInt(0)) 133 | require.NoError(t, err) 134 | 135 | // set new auth claim 136 | user.AuthClaim = authClaim2 137 | user.PK = pk2 138 | 139 | } 140 | 141 | err = gisTree.Add(context.Background(), user.IDHash(t), user.State(t)) 142 | require.NoError(t, err) 143 | 144 | } 145 | 146 | // user 147 | authMTProof := user.AuthMTPStrign(t) 148 | 149 | authNonRevMTProof, nodeAuxNonRev := user.ClaimRevMTP(t, user.AuthClaim) 150 | 151 | sig := user.Sign(challenge) 152 | 153 | gistProofRaw, _, err := gisTree.GenerateProof(context.Background(), user.IDHash(t), nil) 154 | require.NoError(t, err) 155 | 156 | gistRoot := gisTree.Root() 157 | gistProof, gistNodAux := utils.PrepareProof(gistProofRaw, utils.GistLevels) 158 | 159 | inputs := AuthV3Inputs{ 160 | UserGenesisID: user.ID.BigInt().String(), 161 | Nonce: nonce.String(), 162 | UserAuthClaim: user.AuthClaim, 163 | UserAuthClaimMtp: authMTProof, 164 | UserAuthClaimNonRevMtp: authNonRevMTProof, 165 | UserAuthClaimNonRevMtpAuxHi: nodeAuxNonRev.Key, 166 | UserAuthClaimNonRevMtpAuxHv: nodeAuxNonRev.Value, 167 | UserAuthClaimNonRevMtpNoAux: nodeAuxNonRev.NoAux, 168 | Challenge: challenge.String(), 169 | ChallengeSignatureR8X: sig.R8.X.String(), 170 | ChallengeSignatureR8Y: sig.R8.Y.String(), 171 | ChallengeSignatureS: sig.S.String(), 172 | UserClaimsTreeRoot: user.Clt.Root().BigInt().String(), 173 | UserRevTreeRoot: user.Ret.Root().BigInt().String(), 174 | UserRootsTreeRoot: user.Rot.Root().BigInt().String(), 175 | UserState: user.State(t).String(), 176 | GistRoot: gistRoot.BigInt().String(), 177 | GistMtp: gistProof, 178 | GistMtpAuxHi: gistNodAux.Key, 179 | GistMtpAuxHv: gistNodAux.Value, 180 | GistMtpNoAux: gistNodAux.NoAux, 181 | } 182 | 183 | out := AuthV3Outputs{ 184 | ID: userProfile.BigInt().String(), 185 | Challenge: challenge.String(), 186 | GistRoot: gistRoot.BigInt().String(), 187 | } 188 | 189 | json, err := json2.Marshal(TestDataAuthV3{ 190 | desc, 191 | inputs, 192 | out, 193 | }) 194 | require.NoError(t, err) 195 | 196 | utils.SaveTestVector(t, fileName, string(json)) 197 | } 198 | 199 | func TestTre(t *testing.T) { 200 | 201 | tree, err := merkletree.NewMerkleTree(context.Background(), memory.NewMemoryStorage(), 40) 202 | require.Nil(t, err) 203 | 204 | X, ok := new(big.Int).SetString("17640206035128972995519606214765283372613874593503528180869261482403155458945", 10) 205 | require.True(t, ok) 206 | 207 | Y, ok := new(big.Int).SetString("20634138280259599560273310290025659992320584624461316485434108770067472477956", 10) 208 | require.True(t, ok) 209 | 210 | var schemaHash core.SchemaHash 211 | schemaEncodedBytes, _ := hex.DecodeString("ca938857241db9451ea329256b9c06e5") // V1 212 | //schemaEncodedBytes, _ := hex.DecodeString("013fd3f623559d850fb5b02ff012d0e2") // V2 213 | copy(schemaHash[:], schemaEncodedBytes) 214 | 215 | // NOTE: We take nonce as hash of public key to make it random 216 | // We don't use random number here because this test vectors will be used for tests 217 | // and have randomization inside tests is usually a bad idea 218 | revNonce := uint64(15930428023331155902) 219 | require.NoError(t, err) 220 | 221 | claim, err := core.NewClaim(schemaHash, 222 | core.WithIndexDataInts(X, Y), 223 | core.WithRevocationNonce(revNonce)) 224 | require.NoError(t, err) 225 | 226 | marshal, err := json2.Marshal(claim) 227 | require.NoError(t, err) 228 | fmt.Println(string(marshal)) 229 | 230 | hi, hv, err := claim.HiHv() 231 | require.NoError(t, err) 232 | 233 | fmt.Println("hi", hi) 234 | fmt.Println("hv", hv) 235 | 236 | err = tree.Add(context.Background(), hi, hv) 237 | require.NoError(t, err) 238 | 239 | fmt.Println("root", tree.Root().BigInt()) 240 | 241 | clr, _ := new(big.Int).SetString("9763429684850732628215303952870004997159843236039795272605841029866455670219", 10) 242 | state, err := core.IdenState(clr, big.NewInt(0), big.NewInt(0)) 243 | require.NoError(t, err) 244 | 245 | typ, err := core.BuildDIDType(core.DIDMethodIden3, core.Polygon, core.Mumbai) 246 | require.NoError(t, err) 247 | id, err := core.NewIDFromIdenState(typ, state) 248 | require.NoError(t, err) 249 | 250 | fmt.Println("id", id.BigInt()) 251 | fmt.Println("id", id.String()) 252 | 253 | did, err := core.ParseDIDFromID(*id) 254 | require.NoError(t, err) 255 | fmt.Println("did", did.String()) 256 | 257 | //{“schema":"ca938857241db9451ea329256b9c06e5", 258 | //"nonce":"15930428023331155902"," + 259 | //"indexSlotA":"17640206035128972995519606214765283372613874593503528180869261482403155458945", 260 | // "indexSlotB":"20634138280259599560273310290025659992320584624461316485434108770067472477956"} 261 | } 262 | -------------------------------------------------------------------------------- /testvectorgen/go.mod: -------------------------------------------------------------------------------- 1 | module test 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/iden3/go-iden3-core/v2 v2.1.0 7 | github.com/iden3/go-iden3-crypto v0.0.16 8 | github.com/iden3/go-merkletree-sql/v2 v2.0.6 9 | github.com/iden3/go-schema-processor/v2 v2.3.3 10 | github.com/stretchr/testify v1.9.0 11 | ) 12 | 13 | require ( 14 | github.com/davecgh/go-spew v1.1.1 // indirect 15 | github.com/dchest/blake512 v1.0.0 // indirect 16 | github.com/ethereum/go-ethereum v1.13.15 17 | github.com/holiman/uint256 v1.2.4 // indirect 18 | github.com/mr-tron/base58 v1.2.0 // indirect 19 | github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f // indirect 20 | github.com/pkg/errors v0.9.1 // indirect 21 | github.com/pmezard/go-difflib v1.0.0 // indirect 22 | github.com/pquerna/cachecontrol v0.2.0 // indirect 23 | golang.org/x/crypto v0.22.0 // indirect 24 | golang.org/x/sys v0.19.0 // indirect 25 | gopkg.in/yaml.v3 v3.0.1 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /testvectorgen/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/dchest/blake512 v1.0.0 h1:oDFEQFIqFSeuA34xLtXZ/rWxCXdSjirjzPhey5EUvmA= 5 | github.com/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzhLMB92JI= 6 | github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo= 7 | github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= 8 | github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= 9 | github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= 10 | github.com/iden3/go-iden3-core/v2 v2.1.0 h1:R1s7Tj3tIx5lDy8S7OJrSNuxXIFeRzWRmTBaQoQHJps= 11 | github.com/iden3/go-iden3-core/v2 v2.1.0/go.mod h1:L9PxhWPvoS9qTb3inEkZBm1RpjHBt+VTwvxssdzbAdw= 12 | github.com/iden3/go-iden3-crypto v0.0.16 h1:zN867xiz6HgErXVIV/6WyteGcOukE9gybYTorBMEdsk= 13 | github.com/iden3/go-iden3-crypto v0.0.16/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= 14 | github.com/iden3/go-merkletree-sql/v2 v2.0.6 h1:vsVDImnvnHf7Ggr45ptFOXJyWNA/8IwVQO1jzRLUlY8= 15 | github.com/iden3/go-merkletree-sql/v2 v2.0.6/go.mod h1:kRhHKYpui5DUsry5RpveP6IC4XMe6iApdV9VChRYuEk= 16 | github.com/iden3/go-schema-processor/v2 v2.3.3 h1:GfChxMZHG4miA3p/5rLIrM7TGmKu/oAAXgLloYTBHSI= 17 | github.com/iden3/go-schema-processor/v2 v2.3.3/go.mod h1:8y/R0iQpYhyhRQ3sL4F5Aja3+1T68M6uwGQdC4pQ4X0= 18 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 19 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 20 | github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= 21 | github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= 22 | github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 23 | github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f h1:HlPa7RcxTCrva5izPfTEfvYecO7LTahgmMRD1Qp13xg= 24 | github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs= 25 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 26 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 27 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 28 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 29 | github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k= 30 | github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= 31 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 32 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 33 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 34 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 35 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 36 | golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= 37 | golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= 38 | golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= 39 | golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 40 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 41 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 42 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 43 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 44 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 45 | -------------------------------------------------------------------------------- /testvectorgen/statetransition/stateTransition_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | json2 "encoding/json" 5 | "math/big" 6 | "testing" 7 | 8 | "test/utils" 9 | 10 | core "github.com/iden3/go-iden3-core/v2" 11 | "github.com/iden3/go-iden3-crypto/poseidon" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | const ( 16 | userPK = "28156abe7fe2fd433dc9df969286b96666489bac508612d0e16593e944c4f69e" 17 | userPK2 = "28156abe7fe2fd433dc9df969286b96666489bac508612d0e16593e944c4f69d" 18 | ethAddress = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" 19 | 20 | timestamp = "1642074362" 21 | ) 22 | 23 | type StateTransitionInputs struct { 24 | AuthClaim *core.Claim `json:"authClaim"` 25 | AuthClaimMtp []string `json:"authClaimMtp"` 26 | AuthClaimNonRevMtp []string `json:"authClaimNonRevMtp"` 27 | AuthClaimNonRevMtpAuxHi string `json:"authClaimNonRevMtpAuxHi"` 28 | AuthClaimNonRevMtpAuxHv string `json:"authClaimNonRevMtpAuxHv"` 29 | AuthClaimNonRevMtpNoAux string `json:"authClaimNonRevMtpNoAux"` 30 | ClaimsTreeRoot string `json:"claimsTreeRoot"` 31 | IsOldStateGenesis string `json:"isOldStateGenesis"` 32 | NewUserState string `json:"newUserState"` 33 | OldUserState string `json:"oldUserState"` 34 | RevTreeRoot string `json:"revTreeRoot"` 35 | RootsTreeRoot string `json:"rootsTreeRoot"` 36 | SignatureR8X string `json:"signatureR8x"` 37 | SignatureR8Y string `json:"signatureR8y"` 38 | SignatureS string `json:"signatureS"` 39 | UserID string `json:"userID"` 40 | NewAuthClaimMtp []string `json:"newAuthClaimMtp"` 41 | NewClaimsTreeRoot string `json:"newClaimsTreeRoot"` 42 | NewRevTreeRoot string `json:"newRevTreeRoot"` 43 | NewRootsTreeRoot string `json:"newRootsTreeRoot"` 44 | } 45 | 46 | type StateTransitionOutputs struct { 47 | ID string `json:"userID"` 48 | NewUserState string `json:"newUserState"` 49 | OldUserState string `json:"oldUserState"` 50 | IsOldStateGenesis string `json:"isOldStateGenesis"` 51 | } 52 | 53 | type TestDataStateTransition struct { 54 | Desc string `json:"desc"` 55 | In StateTransitionInputs `json:"inputs"` 56 | Out StateTransitionOutputs `json:"expOut"` 57 | } 58 | 59 | func Test_GenesisState(t *testing.T) { 60 | 61 | desc := "Positive: old state is genesis" 62 | isUserStateGenesis := false 63 | 64 | generateAuthTestData(t, isUserStateGenesis, desc, "genesis_state") 65 | } 66 | 67 | func Test_NotGenesis(t *testing.T) { 68 | 69 | desc := "Positive: old state is not genesis" 70 | isUserStateGenesis := true 71 | 72 | generateAuthTestData(t, isUserStateGenesis, desc, "not_genesis_state") 73 | } 74 | 75 | func generateAuthTestData(t *testing.T, genesis bool, desc, fileName string) { 76 | 77 | user := utils.NewIdentity(t, userPK) 78 | 79 | isGenesis := "1" 80 | 81 | // user 82 | authMTProof := user.AuthMTPStrign(t) 83 | 84 | authNonRevMTProof, nodeAuxNonRev := user.ClaimRevMTP(t, user.AuthClaim) 85 | 86 | oldState := user.State(t) // old state is genesis 87 | oldCltRoot := user.Clt.Root().BigInt().String() 88 | oldRevRoot := user.Ret.Root().BigInt().String() 89 | oldRotRoot := user.Rot.Root().BigInt().String() 90 | 91 | //if genesis == false { 92 | // extract pubKey 93 | authClaim2, _ := utils.NewAuthClaim(t, userPK2) 94 | 95 | user.AddClaim(t, authClaim2) 96 | 97 | if genesis { 98 | isGenesis = "0" 99 | 100 | oldState = user.State(t) // old state is genesis 101 | oldCltRoot = user.Clt.Root().BigInt().String() 102 | oldRevRoot = user.Ret.Root().BigInt().String() 103 | oldRotRoot = user.Rot.Root().BigInt().String() 104 | authMTProof = user.AuthMTPStrign(t) 105 | 106 | authNonRevMTProof, nodeAuxNonRev = user.ClaimRevMTP(t, user.AuthClaim) 107 | 108 | claim1 := utils.DefaultUserClaim(t, user.ID, nil) 109 | 110 | user.AddClaim(t, claim1) 111 | } 112 | 113 | newAuthMTProof := user.AuthMTPStrign(t) 114 | newCltRoot := user.Clt.Root().BigInt().String() 115 | newRevRoot := user.Ret.Root().BigInt().String() 116 | newRotRoot := user.Rot.Root().BigInt().String() 117 | 118 | hashOldAndNewStates, err := poseidon.Hash( 119 | []*big.Int{oldState, user.State(t)}) 120 | require.NoError(t, err) 121 | 122 | sig := user.Sign(hashOldAndNewStates) 123 | require.NoError(t, err) 124 | 125 | inputs := StateTransitionInputs{ 126 | AuthClaim: user.AuthClaim, 127 | AuthClaimMtp: authMTProof, 128 | AuthClaimNonRevMtp: authNonRevMTProof, 129 | AuthClaimNonRevMtpAuxHi: nodeAuxNonRev.Key, 130 | AuthClaimNonRevMtpAuxHv: nodeAuxNonRev.Value, 131 | AuthClaimNonRevMtpNoAux: nodeAuxNonRev.NoAux, 132 | ClaimsTreeRoot: oldCltRoot, 133 | RevTreeRoot: oldRevRoot, 134 | RootsTreeRoot: oldRotRoot, 135 | IsOldStateGenesis: isGenesis, 136 | NewUserState: user.State(t).String(), 137 | OldUserState: oldState.String(), 138 | SignatureR8X: sig.R8.X.String(), 139 | SignatureR8Y: sig.R8.Y.String(), 140 | SignatureS: sig.S.String(), 141 | UserID: user.ID.BigInt().String(), 142 | NewAuthClaimMtp: newAuthMTProof, 143 | NewClaimsTreeRoot: newCltRoot, 144 | NewRevTreeRoot: newRevRoot, 145 | NewRootsTreeRoot: newRotRoot, 146 | } 147 | 148 | out := StateTransitionOutputs{ 149 | ID: user.ID.BigInt().String(), 150 | NewUserState: user.State(t).String(), 151 | OldUserState: oldState.String(), 152 | IsOldStateGenesis: isGenesis, 153 | } 154 | 155 | json, err := json2.Marshal(TestDataStateTransition{ 156 | desc, 157 | inputs, 158 | out, 159 | }) 160 | require.NoError(t, err) 161 | 162 | utils.SaveTestVector(t, fileName, string(json)) 163 | } 164 | -------------------------------------------------------------------------------- /testvectorgen/utils/constants.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import _ "embed" 4 | 5 | // List of available operators. 6 | const ( 7 | NOOP int = iota // No operation, skip query verification in circuit 8 | EQ 9 | LT 10 | GT 11 | IN 12 | NIN 13 | NE 14 | LTE 15 | GTE 16 | BETWEEN 17 | NOT_BETWEEN 18 | EXISTS 19 | SD = 16 20 | ) 21 | 22 | var ( 23 | w3cSchemaURL = "https://www.w3.org/2018/credentials/v1" 24 | //go:embed w3cSchema.json 25 | w3cSchemaBody []byte 26 | ) 27 | 28 | const TestClaimDocument = `{ 29 | "@context": [ 30 | "https://www.w3.org/2018/credentials/v1", 31 | "https://w3id.org/citizenship/v1", 32 | "https://w3id.org/security/bbs/v1" 33 | ], 34 | "id": "https://issuer.oidp.uscis.gov/credentials/83627465", 35 | "type": ["VerifiableCredential", "PermanentResidentCard"], 36 | "issuer": "did:example:489398593", 37 | "identifier": 83627465, 38 | "name": "Permanent Resident Card", 39 | "description": "Government of Example Permanent Resident Card.", 40 | "issuanceDate": "2019-12-03T12:19:52Z", 41 | "expirationDate": "2029-12-03T12:19:52Z", 42 | "credentialSubject": { 43 | "id": "did:example:b34ca6cd37bbf23", 44 | "type": ["PermanentResident", "Person"], 45 | "givenName": "JOHN", 46 | "familyName": "SMITH", 47 | "gender": "Male", 48 | "image": "data:image/png;base64,iVBORw0KGgokJggg==", 49 | "residentSince": "2015-01-01", 50 | "lprCategory": "C09", 51 | "lprNumber": "999-999-999", 52 | "commuterClassification": "C1", 53 | "birthCountry": "Bahamas", 54 | "birthDate": "1958-07-17" 55 | } 56 | }` 57 | 58 | const TestNormalClaimDocument = ` 59 | { 60 | "id": "urn:uuid:97fbd3d0-8eb7-11ee-8085-a27b3ddbdc29", 61 | "@context": [ 62 | "https://www.w3.org/2018/credentials/v1", 63 | "https://schema.iden3.io/core/jsonld/iden3proofs.jsonld", 64 | "https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v4.jsonld" 65 | ], 66 | "type": [ 67 | "VerifiableCredential", 68 | "KYCAgeCredential" 69 | ], 70 | "expirationDate": "2361-03-21T21:14:48+02:00", 71 | "issuanceDate": "2023-11-29T15:02:47.508637+02:00", 72 | "credentialSubject": { 73 | "birthday": 19960424, 74 | "documentType": 2, 75 | "id": "did:polygonid:polygon:mumbai:2qDAxLyxvGaGqBLmoDHwohHjtevVdYvpnWcYiK6AcK", 76 | "type": "KYCAgeCredential" 77 | }, 78 | "credentialStatus": { 79 | "id": "http://localhost:8001/api/v1/identities/did%3Apolygonid%3Apolygon%3Amumbai%3A2qGF2TDJZLxzNU1mP3x5PwcUF43vMgdrhRQhaM2HnG/claims/revocation/status/239691578", 80 | "revocationNonce": 239691578, 81 | "type": "SparseMerkleTreeProof" 82 | }, 83 | "issuer": "did:polygonid:polygon:mumbai:2qGF2TDJZLxzNU1mP3x5PwcUF43vMgdrhRQhaM2HnG", 84 | "credentialSchema": { 85 | "id": "https://raw.githubusercontent.com/iden3/claim-schema-vocab/test/cred-array/schemas/json/KYCAgeCredential-v4-arr-list.json", 86 | "type": "JsonSchema2023" 87 | }}` 88 | 89 | const ( 90 | IdentityTreeLevels = 40 91 | GistLevels = 64 92 | ClaimLevels = 32 93 | ) 94 | -------------------------------------------------------------------------------- /testvectorgen/utils/identity.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "math/big" 6 | "testing" 7 | 8 | "github.com/ethereum/go-ethereum/common" 9 | core "github.com/iden3/go-iden3-core/v2" 10 | "github.com/iden3/go-iden3-crypto/babyjub" 11 | "github.com/iden3/go-iden3-crypto/poseidon" 12 | "github.com/iden3/go-merkletree-sql/v2" 13 | "github.com/iden3/go-merkletree-sql/v2/db/memory" 14 | ) 15 | 16 | type IdentityTest struct { 17 | ID core.ID 18 | Clt *merkletree.MerkleTree 19 | Ret *merkletree.MerkleTree 20 | Rot *merkletree.MerkleTree 21 | AuthClaim *core.Claim 22 | PK *babyjub.PrivateKey 23 | } 24 | 25 | func (it *IdentityTest) Sign(challenge *big.Int) *babyjub.Signature { 26 | return it.PK.SignPoseidon(challenge) 27 | } 28 | 29 | func (it *IdentityTest) State(t testing.TB) *big.Int { 30 | state, err := core.IdenState(it.Clt.Root().BigInt(), it.Ret.Root().BigInt(), it.Rot.Root().BigInt()) 31 | if err != nil { 32 | t.Fatalf("Error calculating state: %v", err) 33 | } 34 | return state 35 | } 36 | 37 | func (it *IdentityTest) AuthMTPStrign(t testing.TB) []string { 38 | p, _ := it.ClaimMTPRaw(t, it.AuthClaim) 39 | return PrepareSiblingsStr(p.AllSiblings(), IdentityTreeLevels) 40 | } 41 | 42 | func (it *IdentityTest) SignClaim(t testing.TB, claim *core.Claim) *babyjub.Signature { 43 | hashIndex, hashValue, err := claim.HiHv() 44 | if err != nil { 45 | t.Fatalf("can't get hash index/value from claim %v", err) 46 | } 47 | 48 | commonHash, err := poseidon.Hash([]*big.Int{hashIndex, hashValue}) 49 | if err != nil { 50 | t.Fatalf("can't hash index and value") 51 | } 52 | 53 | return it.PK.SignPoseidon(commonHash) 54 | } 55 | 56 | func (it *IdentityTest) ClaimMTPRaw(t testing.TB, claim *core.Claim) (*merkletree.Proof, *big.Int) { 57 | // add auth claim to claimsMT 58 | hi, _, err := claim.HiHv() 59 | if err != nil { 60 | t.Fatalf("can't get claim hash index %v", err) 61 | } 62 | 63 | proof, value, err := it.Clt.GenerateProof(context.Background(), hi, nil) 64 | if err != nil { 65 | t.Fatalf("can't generate proof %v", err) 66 | } 67 | return proof, value 68 | } 69 | 70 | func (it *IdentityTest) ClaimMTP(t testing.TB, claim *core.Claim) (sibling []string, nodeAux NodeAuxValue) { 71 | // add auth claim to claimsMT 72 | hi, _, err := claim.HiHv() 73 | if err != nil { 74 | t.Fatalf("can't get claim index hash %v", err) 75 | } 76 | 77 | proof, _, err := it.Clt.GenerateProof(context.Background(), hi, nil) 78 | if err != nil { 79 | t.Fatalf("can't generate proof %v", err) 80 | } 81 | 82 | return PrepareProof(proof, IdentityTreeLevels) 83 | } 84 | 85 | func (it *IdentityTest) ClaimRevMTPRaw(t testing.TB, claim *core.Claim) (*merkletree.Proof, *big.Int) { 86 | // add auth claim to claimsMT 87 | revNonce := claim.GetRevocationNonce() 88 | 89 | proof, value, err := it.Ret.GenerateProof(context.Background(), new(big.Int).SetUint64(revNonce), nil) 90 | if err != nil { 91 | t.Fatalf("can't generate proof %v", err) 92 | } 93 | return proof, value 94 | } 95 | 96 | func (it *IdentityTest) ClaimRevMTP(t testing.TB, claim *core.Claim) (sibling []string, nodeAux NodeAuxValue) { 97 | // add auth claim to claimsMT 98 | revNonce := claim.GetRevocationNonce() 99 | 100 | proof, _, err := it.Ret.GenerateProof(context.Background(), new(big.Int).SetUint64(revNonce), nil) 101 | if err != nil { 102 | t.Fatalf("can't generate proof %v", err) 103 | } 104 | 105 | return PrepareProof(proof, IdentityTreeLevels) 106 | 107 | } 108 | 109 | func (it *IdentityTest) IDHash(t testing.TB) *big.Int { 110 | idHash, err := poseidon.Hash([]*big.Int{it.ID.BigInt()}) 111 | if err != nil { 112 | t.Fatalf("can't hash id %v", err) 113 | } 114 | return idHash 115 | } 116 | 117 | func (it *IdentityTest) AddClaim(t *testing.T, claim *core.Claim) { 118 | // add auth claim to claimsMT 119 | hi, hv, err := claim.HiHv() 120 | if err != nil { 121 | t.Fatalf("Error calculating hi and hv: %v", err) 122 | } 123 | 124 | err = it.Clt.Add(context.Background(), hi, hv) 125 | if err != nil { 126 | t.Fatalf("Error adding claim to claimsMT: %v", err) 127 | } 128 | } 129 | 130 | func NewIdentity(t testing.TB, privKHex string) *IdentityTest { 131 | 132 | it := IdentityTest{} 133 | var err error 134 | 135 | // init claims tree 136 | 137 | it.Clt, err = merkletree.NewMerkleTree(context.Background(), memory.NewMemoryStorage(), IdentityTreeLevels) 138 | if err != nil { 139 | t.Fatalf("Error creating Claims merkle tree: %v", err) 140 | } 141 | it.Ret, err = merkletree.NewMerkleTree(context.Background(), memory.NewMemoryStorage(), IdentityTreeLevels) 142 | if err != nil { 143 | t.Fatalf("Error creating Revocation merkle tree: %v", err) 144 | } 145 | it.Rot, err = merkletree.NewMerkleTree(context.Background(), memory.NewMemoryStorage(), IdentityTreeLevels) 146 | if err != nil { 147 | t.Fatalf("Error creating Roots merkle tree: %v", err) 148 | } 149 | 150 | authClaim, key := NewAuthClaim(t, privKHex) 151 | 152 | it.AuthClaim = authClaim 153 | it.PK = key 154 | 155 | // add auth claim to claimsMT 156 | hi, hv, err := authClaim.HiHv() 157 | 158 | err = it.Clt.Add(context.Background(), hi, hv) 159 | if err != nil { 160 | t.Fatalf("Error adding Auth claim to Claims merkle tree: %v", err) 161 | } 162 | 163 | state := it.State(t) 164 | 165 | identifier, err := IDFromState(state) 166 | if err != nil { 167 | t.Fatalf("Error generating id from state: %v", err) 168 | } 169 | 170 | it.ID = *identifier 171 | 172 | return &it 173 | } 174 | 175 | func NewEthereumBasedIdentity(t testing.TB, ethAddr string) *IdentityTest { 176 | 177 | it := IdentityTest{} 178 | var err error 179 | 180 | // init claims tree 181 | 182 | it.Clt, err = merkletree.NewMerkleTree(context.Background(), memory.NewMemoryStorage(), IdentityTreeLevels) 183 | if err != nil { 184 | t.Fatalf("Error creating Claims merkle tree: %v", err) 185 | } 186 | it.Ret, err = merkletree.NewMerkleTree(context.Background(), memory.NewMemoryStorage(), IdentityTreeLevels) 187 | if err != nil { 188 | t.Fatalf("Error creating Revocation merkle tree: %v", err) 189 | } 190 | it.Rot, err = merkletree.NewMerkleTree(context.Background(), memory.NewMemoryStorage(), IdentityTreeLevels) 191 | if err != nil { 192 | t.Fatalf("Error creating Roots merkle tree: %v", err) 193 | } 194 | 195 | addr := common.HexToAddress(ethAddr) 196 | currentState := core.GenesisFromEthAddress(addr) 197 | if err != nil { 198 | t.Fatalf("Error creating genesis state from address: %v", err) 199 | } 200 | 201 | didType, err := core.BuildDIDType(core.DIDMethodPolygonID, core.Polygon, core.Mumbai) 202 | if err != nil { 203 | t.Fatalf("Error creating did type: %v", err) 204 | } 205 | 206 | did, err := core.NewDID(didType, currentState) 207 | if err != nil { 208 | t.Fatalf("Error creating new did : %v", err) 209 | } 210 | 211 | it.ID, err = core.IDFromDID(*did) 212 | if err != nil { 213 | t.Fatalf("Error creating id from did: %v", err) 214 | } 215 | 216 | return &it 217 | } 218 | 219 | type NodeAuxValue struct { 220 | Key string 221 | Value string 222 | NoAux string 223 | } 224 | 225 | func getNodeAuxValue(p *merkletree.Proof) NodeAuxValue { 226 | 227 | // proof of inclusion 228 | if p.Existence { 229 | return NodeAuxValue{ 230 | Key: merkletree.HashZero.BigInt().String(), 231 | Value: merkletree.HashZero.BigInt().String(), 232 | NoAux: "0", 233 | } 234 | } 235 | 236 | // proof of non-inclusion (NodeAux exists) 237 | if p.NodeAux != nil && p.NodeAux.Value != nil && p.NodeAux.Key != nil { 238 | return NodeAuxValue{ 239 | Key: p.NodeAux.Key.BigInt().String(), 240 | Value: p.NodeAux.Value.BigInt().String(), 241 | NoAux: "0", 242 | } 243 | } 244 | // proof of non-inclusion (NodeAux does not exist) 245 | return NodeAuxValue{ 246 | Key: merkletree.HashZero.BigInt().String(), 247 | Value: merkletree.HashZero.BigInt().String(), 248 | NoAux: "1", 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /testvectorgen/utils/identity_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | json2 "encoding/json" 5 | "math/big" 6 | "testing" 7 | 8 | core "github.com/iden3/go-iden3-core/v2" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | const userPK = "28156abe7fe2fd433dc9df969286b96666489bac508612d0e16593e944c4f69e" 13 | 14 | func Test_IdentityData(t *testing.T) { 15 | 16 | id := NewIdentity(t, userPK) 17 | 18 | r := struct { 19 | IssuerID *big.Int `json:"issuerID"` 20 | IssuerAuthClaim *core.Claim `json:"issuerAuthClaim"` 21 | IssuerAuthClaimMtp []string `json:"issuerAuthClaimMtp"` 22 | IssuerAuthClaimsTreeRoot *big.Int `json:"issuerAuthClaimsTreeRoot"` 23 | IssuerAuthRevTreeRoot *big.Int `json:"issuerAuthRevTreeRoot"` 24 | IssuerAuthRootsTreeRoot *big.Int `json:"issuerAuthRootsTreeRoot"` 25 | IssuerAuthState *big.Int `json:"issuerAuthState"` 26 | }{ 27 | IssuerID: id.ID.BigInt(), 28 | IssuerAuthClaim: id.AuthClaim, 29 | IssuerAuthClaimMtp: id.AuthMTPStrign(t), 30 | IssuerAuthClaimsTreeRoot: id.Clt.Root().BigInt(), 31 | IssuerAuthRevTreeRoot: id.Ret.Root().BigInt(), 32 | IssuerAuthRootsTreeRoot: id.Rot.Root().BigInt(), 33 | IssuerAuthState: id.State(t), 34 | } 35 | 36 | json, err := json2.Marshal(r) 37 | t.Log(string(json)) 38 | 39 | t.Log("ID:", id.ID.String()) 40 | t.Log("ID int:", id.ID.BigInt().String()) 41 | did, err := core.ParseDIDFromID(id.ID) 42 | require.NoError(t, err) 43 | 44 | t.Log("DID:", did.String()) 45 | } 46 | -------------------------------------------------------------------------------- /testvectorgen/utils/w3cSchema.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "@version": 1.1, 4 | "@protected": true, 5 | 6 | "id": "@id", 7 | "type": "@type", 8 | 9 | "VerifiableCredential": { 10 | "@id": "https://www.w3.org/2018/credentials#VerifiableCredential", 11 | "@context": { 12 | "@version": 1.1, 13 | "@protected": true, 14 | 15 | "id": "@id", 16 | "type": "@type", 17 | 18 | "cred": "https://www.w3.org/2018/credentials#", 19 | "sec": "https://w3id.org/security#", 20 | "xsd": "http://www.w3.org/2001/XMLSchema#", 21 | 22 | "credentialSchema": { 23 | "@id": "cred:credentialSchema", 24 | "@type": "@id", 25 | "@context": { 26 | "@version": 1.1, 27 | "@protected": true, 28 | 29 | "id": "@id", 30 | "type": "@type", 31 | 32 | "cred": "https://www.w3.org/2018/credentials#", 33 | 34 | "JsonSchemaValidator2018": "cred:JsonSchemaValidator2018" 35 | } 36 | }, 37 | "credentialStatus": {"@id": "cred:credentialStatus", "@type": "@id"}, 38 | "credentialSubject": {"@id": "cred:credentialSubject", "@type": "@id"}, 39 | "evidence": {"@id": "cred:evidence", "@type": "@id"}, 40 | "expirationDate": {"@id": "cred:expirationDate", "@type": "xsd:dateTime"}, 41 | "holder": {"@id": "cred:holder", "@type": "@id"}, 42 | "issued": {"@id": "cred:issued", "@type": "xsd:dateTime"}, 43 | "issuer": {"@id": "cred:issuer", "@type": "@id"}, 44 | "issuanceDate": {"@id": "cred:issuanceDate", "@type": "xsd:dateTime"}, 45 | "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, 46 | "refreshService": { 47 | "@id": "cred:refreshService", 48 | "@type": "@id", 49 | "@context": { 50 | "@version": 1.1, 51 | "@protected": true, 52 | 53 | "id": "@id", 54 | "type": "@type", 55 | 56 | "cred": "https://www.w3.org/2018/credentials#", 57 | 58 | "ManualRefreshService2018": "cred:ManualRefreshService2018" 59 | } 60 | }, 61 | "termsOfUse": {"@id": "cred:termsOfUse", "@type": "@id"}, 62 | "validFrom": {"@id": "cred:validFrom", "@type": "xsd:dateTime"}, 63 | "validUntil": {"@id": "cred:validUntil", "@type": "xsd:dateTime"} 64 | } 65 | }, 66 | 67 | "VerifiablePresentation": { 68 | "@id": "https://www.w3.org/2018/credentials#VerifiablePresentation", 69 | "@context": { 70 | "@version": 1.1, 71 | "@protected": true, 72 | 73 | "id": "@id", 74 | "type": "@type", 75 | 76 | "cred": "https://www.w3.org/2018/credentials#", 77 | "sec": "https://w3id.org/security#", 78 | 79 | "holder": {"@id": "cred:holder", "@type": "@id"}, 80 | "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, 81 | "verifiableCredential": {"@id": "cred:verifiableCredential", "@type": "@id", "@container": "@graph"} 82 | } 83 | }, 84 | 85 | "EcdsaSecp256k1Signature2019": { 86 | "@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019", 87 | "@context": { 88 | "@version": 1.1, 89 | "@protected": true, 90 | 91 | "id": "@id", 92 | "type": "@type", 93 | 94 | "sec": "https://w3id.org/security#", 95 | "xsd": "http://www.w3.org/2001/XMLSchema#", 96 | 97 | "challenge": "sec:challenge", 98 | "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, 99 | "domain": "sec:domain", 100 | "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, 101 | "jws": "sec:jws", 102 | "nonce": "sec:nonce", 103 | "proofPurpose": { 104 | "@id": "sec:proofPurpose", 105 | "@type": "@vocab", 106 | "@context": { 107 | "@version": 1.1, 108 | "@protected": true, 109 | 110 | "id": "@id", 111 | "type": "@type", 112 | 113 | "sec": "https://w3id.org/security#", 114 | 115 | "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, 116 | "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} 117 | } 118 | }, 119 | "proofValue": "sec:proofValue", 120 | "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} 121 | } 122 | }, 123 | 124 | "EcdsaSecp256r1Signature2019": { 125 | "@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019", 126 | "@context": { 127 | "@version": 1.1, 128 | "@protected": true, 129 | 130 | "id": "@id", 131 | "type": "@type", 132 | 133 | "sec": "https://w3id.org/security#", 134 | "xsd": "http://www.w3.org/2001/XMLSchema#", 135 | 136 | "challenge": "sec:challenge", 137 | "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, 138 | "domain": "sec:domain", 139 | "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, 140 | "jws": "sec:jws", 141 | "nonce": "sec:nonce", 142 | "proofPurpose": { 143 | "@id": "sec:proofPurpose", 144 | "@type": "@vocab", 145 | "@context": { 146 | "@version": 1.1, 147 | "@protected": true, 148 | 149 | "id": "@id", 150 | "type": "@type", 151 | 152 | "sec": "https://w3id.org/security#", 153 | 154 | "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, 155 | "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} 156 | } 157 | }, 158 | "proofValue": "sec:proofValue", 159 | "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} 160 | } 161 | }, 162 | 163 | "Ed25519Signature2018": { 164 | "@id": "https://w3id.org/security#Ed25519Signature2018", 165 | "@context": { 166 | "@version": 1.1, 167 | "@protected": true, 168 | 169 | "id": "@id", 170 | "type": "@type", 171 | 172 | "sec": "https://w3id.org/security#", 173 | "xsd": "http://www.w3.org/2001/XMLSchema#", 174 | 175 | "challenge": "sec:challenge", 176 | "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, 177 | "domain": "sec:domain", 178 | "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, 179 | "jws": "sec:jws", 180 | "nonce": "sec:nonce", 181 | "proofPurpose": { 182 | "@id": "sec:proofPurpose", 183 | "@type": "@vocab", 184 | "@context": { 185 | "@version": 1.1, 186 | "@protected": true, 187 | 188 | "id": "@id", 189 | "type": "@type", 190 | 191 | "sec": "https://w3id.org/security#", 192 | 193 | "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, 194 | "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} 195 | } 196 | }, 197 | "proofValue": "sec:proofValue", 198 | "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} 199 | } 200 | }, 201 | 202 | "RsaSignature2018": { 203 | "@id": "https://w3id.org/security#RsaSignature2018", 204 | "@context": { 205 | "@version": 1.1, 206 | "@protected": true, 207 | 208 | "challenge": "sec:challenge", 209 | "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, 210 | "domain": "sec:domain", 211 | "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, 212 | "jws": "sec:jws", 213 | "nonce": "sec:nonce", 214 | "proofPurpose": { 215 | "@id": "sec:proofPurpose", 216 | "@type": "@vocab", 217 | "@context": { 218 | "@version": 1.1, 219 | "@protected": true, 220 | 221 | "id": "@id", 222 | "type": "@type", 223 | 224 | "sec": "https://w3id.org/security#", 225 | 226 | "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, 227 | "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} 228 | } 229 | }, 230 | "proofValue": "sec:proofValue", 231 | "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} 232 | } 233 | }, 234 | 235 | "proof": {"@id": "https://w3id.org/security#proof", "@type": "@id", "@container": "@graph"} 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "moduleResolution": "node", 5 | "resolveJsonModule": true, 6 | "pretty": true, 7 | "declaration": true, 8 | "sourceMap": true, 9 | "target": "es2020", 10 | "outDir": "dist", 11 | "baseUrl": "src", 12 | "esModuleInterop": true 13 | }, 14 | "include": [ 15 | "src/**/*.ts" 16 | ], 17 | "exclude": [ 18 | "node_modules" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "indent": [true, "spaces", 4], 9 | "semicolon": [false, "always"] 10 | }, 11 | "rulesDirectory": [], 12 | "linterOptions": { 13 | "exclude": [ 14 | "node_modules/**" 15 | ] 16 | } 17 | } -------------------------------------------------------------------------------- /update-contract-data-v3.js: -------------------------------------------------------------------------------- 1 | const dataFolder = './testvectorgen/contract_data/testdata/v3'; 2 | const contractDataBaseFolder = '../contracts/test/validators/'; 3 | const buildFolder = './build/'; 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const { execSync, execFileSync } = require('child_process'); 7 | 8 | const files = fs.readdirSync(dataFolder); 9 | 10 | let circuitName = null; 11 | let destinationFolder = null; 12 | for (const file of files) { 13 | 14 | 15 | if (file.includes('state')) { 16 | circuitName = 'stateTransition' 17 | destinationFolder = 'common-data' 18 | } else { 19 | circuitName = 'credentialAtomicQueryV3OnChain-beta.1' 20 | destinationFolder = 'v3/data' 21 | } 22 | const buildPath = `./build/${circuitName}/${circuitName}_js/`; 23 | ['input.json', 'public.json', 'proof.json'].forEach((f) => { 24 | const p = path.join(`./build/${circuitName}/${circuitName}_js`, f) 25 | fs.existsSync(p) && 26 | fs.unlinkSync(p); 27 | console.log(`Deleted file: ${p}`); 28 | }); 29 | const { inputs } = require(`${dataFolder}/${file}`); 30 | console.log(`Reading file: ${dataFolder}/${file}`); 31 | 32 | console.log(`Creating file: ${buildPath}/input.json`); 33 | fs.writeFileSync(`${buildPath}/input.json`, JSON.stringify(inputs), 'utf-8'); 34 | const child = execSync(`./generate.sh ${circuitName}`); 35 | console.log(`execution completed`, new TextDecoder().decode(child)); 36 | const pub_signals = JSON.parse(fs.readFileSync(`${buildPath}/public.json`).toString()); 37 | console.log(pub_signals); 38 | const proof = JSON.parse(fs.readFileSync(`${buildPath}/proof.json`).toString()); 39 | console.log('Writing file: ', `${contractDataBaseFolder}/${destinationFolder}/${file}`); 40 | fs.writeFileSync(`${contractDataBaseFolder}/${destinationFolder}/${file}`, JSON.stringify({ 41 | pub_signals, 42 | proof 43 | }), 'utf-8'); 44 | 45 | } 46 | 47 | console.log('Done'); 48 | -------------------------------------------------------------------------------- /update-contract-data.js: -------------------------------------------------------------------------------- 1 | const dataFolder = './testvectorgen/contract_data/testdata/v3'; 2 | const contractDataBaseFolder = '../contracts/test/validators/v'; 3 | const buildFolder = './build/'; 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const { execSync, execFileSync } = require('child_process'); 7 | 8 | const files = fs.readdirSync(dataFolder); 9 | 10 | let circuitName = null; 11 | let destinationFolder = null; 12 | for (const file of files) { 13 | 14 | 15 | if (file.includes('state')) { 16 | circuitName = 'stateTransition' 17 | destinationFolder = 'common-data' 18 | } else if (file.includes('sig')) { 19 | circuitName = 'credentialAtomicQuerySigV2OnChain' 20 | destinationFolder = 'sig/data' 21 | } else if (file.includes('mtp')) { 22 | circuitName = 'credentialAtomicQueryMTPV2OnChain' 23 | destinationFolder = 'mtp/data' 24 | } else { 25 | throw new Error('unknown circuit') 26 | } 27 | const buildPath = `./build/${circuitName}/${circuitName}_js/`; 28 | ['input.json', 'public.json', 'proof.json'].forEach((f) => { 29 | const p = path.join(`./build/${circuitName}/${circuitName}_js`, f) 30 | fs.existsSync(p) && 31 | fs.unlinkSync(p); 32 | console.log(`Deleted file: ${p}`); 33 | }); 34 | const { inputs } = require(`${dataFolder}/${file}`); 35 | console.log(`Reading file: ${dataFolder}/${file}`); 36 | 37 | console.log(`Creating file: ${buildPath}/input.json`); 38 | fs.writeFileSync(`${buildPath}/input.json`, JSON.stringify(inputs), 'utf-8'); 39 | const child = execSync(`./generate.sh ${circuitName}`); 40 | console.log(`execution completed`, new TextDecoder().decode(child)); 41 | const pub_signals = JSON.parse(fs.readFileSync(`${buildPath}/public.json`).toString()); 42 | console.log(pub_signals); 43 | const proof = JSON.parse(fs.readFileSync(`${buildPath}/proof.json`).toString()); 44 | console.log('Writing file: ', `${contractDataBaseFolder}/${destinationFolder}/${file}`); 45 | fs.writeFileSync(`${contractDataBaseFolder}/${destinationFolder}/${file}`, JSON.stringify({ 46 | pub_signals, 47 | proof 48 | }), 'utf-8'); 49 | 50 | } 51 | 52 | console.log('UPDATE verifiers'); 53 | for (const part of ['MTP', 'Sig']) { 54 | const circuitName = `credentialAtomicQuery${part}V2OnChain`; 55 | const contractName = `${buildFolder}${circuitName}/verifier.sol`; 56 | const contractContent = fs.readFileSync(contractName).toString(); 57 | const newContractContent = contractContent.replace('pragma solidity ^0.6.11;', 'pragma solidity ^0.8.0;').replace('contract Verifier', 'contract Verifier' + part); 58 | fs.writeFileSync(`../contracts/contracts/lib/verifier${part}.sol`, newContractContent, 'utf-8'); 59 | } 60 | console.log('Done'); 61 | --------------------------------------------------------------------------------