├── .nvmrc ├── .prettierrc ├── src ├── circuits │ ├── index.ts │ ├── auth.ts │ ├── ownershipVerifier.ts │ ├── common.ts │ ├── authV2.ts │ ├── registry.ts │ ├── atomicSigV2.ts │ ├── query.ts │ ├── atomicMtpV2.ts │ ├── linkedMultiQuery.ts │ └── atomicV3.ts ├── index.ts ├── state │ ├── types │ │ └── ethers-contracts │ │ │ ├── factories │ │ │ ├── index.ts │ │ │ └── Abi__factory.ts │ │ │ ├── index.ts │ │ │ └── common.ts │ └── resolver.ts ├── types-sdk.ts ├── constants.ts └── auth │ └── auth.ts ├── .gitignore ├── tsconfig.test.json ├── __mocks__ └── @digitalbazaar │ └── http-client │ └── dist │ └── cjs │ └── index.js ├── .eslintrc.js ├── tsconfig.json ├── .github ├── workflows │ ├── npm-publish.yml │ └── ci.yaml └── ISSUE_TEMPLATE │ └── issue-report.md ├── patches └── @digitalbazaar+http-client+3.4.1.patch ├── jest.config.js ├── test ├── genesis.test.ts ├── common.test.ts ├── schema.test.ts ├── mocks.ts ├── cache.test.ts ├── linked-proofs.test.ts └── atomicV3.test.ts ├── LICENSE-MIT ├── package.json ├── Readme.md ├── LICENSE-APACHE └── abi.json /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.19.0 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | "@iden3/eslint-config/prettier" 2 | -------------------------------------------------------------------------------- /src/circuits/index.ts: -------------------------------------------------------------------------------- 1 | export * from '@lib/circuits/registry'; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | coverage 3 | dist 4 | .DS_Store 5 | .idea 6 | .vscode 7 | /test/testdata -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src", "types", "test"] 4 | } 5 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * as auth from '@lib/auth/auth'; 2 | export * as resolver from '@lib/state/resolver'; 3 | export * as protocol from '@lib/types-sdk'; 4 | export { core } from '@0xpolygonid/js-sdk'; 5 | -------------------------------------------------------------------------------- /src/state/types/ethers-contracts/factories/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { Abi__factory } from './Abi__factory'; 5 | -------------------------------------------------------------------------------- /src/circuits/auth.ts: -------------------------------------------------------------------------------- 1 | import { AuthPubSignalsV2 } from './authV2'; 2 | 3 | // This class should be used for auth circuits (authV2/authV3/authV3_8_32) 4 | export class AuthPubSignals extends AuthPubSignalsV2 {} 5 | -------------------------------------------------------------------------------- /src/types-sdk.ts: -------------------------------------------------------------------------------- 1 | export { 2 | AuthorizationRequestMessage, 3 | AuthorizationResponseMessage, 4 | CredentialsOfferMessage, 5 | ZeroKnowledgeProofRequest, 6 | ZeroKnowledgeProofResponse, 7 | PROTOCOL_CONSTANTS 8 | } from '@0xpolygonid/js-sdk'; 9 | -------------------------------------------------------------------------------- /__mocks__/@digitalbazaar/http-client/dist/cjs/index.js: -------------------------------------------------------------------------------- 1 | const mockHttpClient = { 2 | request: jest.fn(), 3 | post: jest.fn(), 4 | get: jest.fn(), 5 | put: jest.fn(), 6 | delete: jest.fn(), 7 | }; 8 | 9 | module.exports = mockHttpClient; 10 | -------------------------------------------------------------------------------- /src/state/types/ethers-contracts/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { Abi } from './Abi'; 5 | export * as factories from './factories'; 6 | export { Abi__factory } from './factories/Abi__factory'; 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const iden3Config = require('@iden3/eslint-config'); 2 | const { spellcheckerRule, cspellConfig } = require('@iden3/eslint-config/cspell'); 3 | 4 | module.exports = { 5 | ...iden3Config, 6 | rules: { 7 | '@cspell/spellchecker': [ 8 | 1, 9 | { 10 | ...spellcheckerRule, 11 | cspell: { 12 | ...cspellConfig, 13 | ignoreWords: ['unmarshal', 'UWLEWdAWcosiLkYoL0KWwZpgEOrPPepl6T5gC'] 14 | } 15 | } 16 | ] 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist/cjs", 4 | "module": "ES2020", 5 | "target": "es2020", 6 | "moduleResolution": "Node", 7 | "resolveJsonModule": true, 8 | "allowSyntheticDefaultImports": true, 9 | "allowJs": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "strict": true, 13 | "typeRoots": ["node_modules/@types"], 14 | "paths": { 15 | "@lib/*": ["./src/*"] 16 | } 17 | }, 18 | "include": ["src", "types"] 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish package to NPM 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | publish-npm: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-node@v3 13 | with: 14 | node-version: 18 15 | registry-url: https://registry.npmjs.org/ 16 | - run: npm ci 17 | - run: npm run build 18 | - run: npm publish 19 | env: 20 | NODE_AUTH_TOKEN: ${{secrets.IDENTITY_NPM_PUBLISH_TOKEN}} 21 | -------------------------------------------------------------------------------- /patches/@digitalbazaar+http-client+3.4.1.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@digitalbazaar/http-client/dist/cjs/index.cjs b/node_modules/@digitalbazaar/http-client/dist/cjs/index.cjs 2 | index c81a996..97954cf 100644 3 | --- a/node_modules/@digitalbazaar/http-client/dist/cjs/index.cjs 4 | +++ b/node_modules/@digitalbazaar/http-client/dist/cjs/index.cjs 5 | @@ -78,7 +78,7 @@ function deferred(f) { 6 | * Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved. 7 | */ 8 | 9 | -const kyOriginalPromise = deferred(() => import('ky-universal') 10 | +const kyOriginalPromise = deferred(() => import('ky') 11 | .then(({default: ky}) => ky)); 12 | 13 | const DEFAULT_HEADERS = { 14 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | const defaultAuthVerifyOpts = 5 * 60 * 1000; // 5 minutes 2 | const defaultStateTransitionDelay = 1 * 60 * 60 * 1000; // 1 hour; 3 | 4 | export const CONSTANTS = { 5 | ACCEPTED_STATE_TRANSITION_DELAY: defaultStateTransitionDelay, 6 | CIRCUITS_ARRAY_VALUE_SIZE: 64, 7 | DEFAULT_CACHE_MAX_SIZE: 10_000, 8 | STATE_CACHE_OPTIONS: { 9 | NOT_REPLACED_TTL: defaultStateTransitionDelay / 2, 10 | REPLACED_TTL: defaultStateTransitionDelay 11 | }, 12 | GIST_ROOT_CACHE_OPTIONS: { 13 | NOT_REPLACED_TTL: defaultAuthVerifyOpts / 2, // 2.5 minutes; 14 | REPLACED_TTL: defaultAuthVerifyOpts // 5 minutes; 15 | }, 16 | AUTH_ACCEPTED_STATE_TRANSITION_DELAY: defaultAuthVerifyOpts 17 | }; 18 | -------------------------------------------------------------------------------- /src/circuits/ownershipVerifier.ts: -------------------------------------------------------------------------------- 1 | import { Id, DID } from '@iden3/js-iden3-core'; 2 | 3 | export abstract class IDOwnershipPubSignals { 4 | userId!: Id; 5 | challenge!: bigint; 6 | async verifyIdOwnership(sender: string, challenge: bigint): Promise { 7 | const senderId = DID.idFromDID(DID.parse(sender)); 8 | if (senderId.string() !== this.userId.string()) { 9 | throw new Error( 10 | `sender id is not used for proof creation, expected ${senderId}, user from public signals: ${this.userId.string()}` 11 | ); 12 | } 13 | if (challenge !== this.challenge) { 14 | throw new Error( 15 | `challenge is not used for proof creation, expected ${challenge}, challenge from public signals: ${this.challenge} ` 16 | ); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testTimeout: 60000, 3 | transform: { 4 | '^.+\\.(t|j)sx?$': ['ts-jest', { useESM: true }] 5 | }, 6 | transformIgnorePatterns: ['/node_modules/(?!((quick-lru)/))'], 7 | testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$', 8 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 9 | moduleNameMapper: { 10 | '@lib/circuits/(.*)': '/src/circuits/$1', 11 | '@lib/proofs/(.*)': '/src/proofs/$1', 12 | '@lib/auth/(.*)': '/src/auth/$1', 13 | '@lib/state/(.*)': '/src/state/$1', 14 | '@lib/cache': '/src/cache', 15 | '@lib/constants': '/src/constants', 16 | '@digitalbazaar/http-client': '/__mocks__/@digitalbazaar/http-client/dist/cjs/index.js' 17 | }, 18 | extensionsToTreatAsEsm: ['.ts'] 19 | }; 20 | -------------------------------------------------------------------------------- /test/genesis.test.ts: -------------------------------------------------------------------------------- 1 | import { DID } from '@iden3/js-iden3-core'; 2 | import { isGenesisStateId } from '@lib/state/resolver'; 3 | 4 | describe('Genesis', () => { 5 | it('isGenesisState', async () => { 6 | const userDIDString = 'did:iden3:polygon:mumbai:x6suHR8HkEYczV9yVeAKKiXCZAd25P8WS6QvNhszk'; 7 | const userDID = DID.parse(userDIDString); 8 | const userID = DID.idFromDID(userDID); 9 | 10 | const genesisState = 11 | '7521024223205616003431860562270429547098131848980857190502964780628723574810'; 12 | const nonGenesisState = 13 | '6017654403209798611575982337826892532952335378376369712724079246845524041042'; 14 | 15 | let isGenesis = isGenesisStateId(userID.bigInt(), BigInt(genesisState)); 16 | expect(isGenesis).toEqual(true); 17 | 18 | isGenesis = isGenesisStateId(userID.bigInt(), BigInt(nonGenesisState)); 19 | expect(isGenesis).toEqual(false); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: Kolezhniuk, vmidyllic 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Install js-iden3-auth@ 16 | 2. call function with param 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Screenshots** 22 | If applicable, add screenshots to help explain your problem. 23 | 24 | ** Environment info (please complete the following information):** 25 | - OS version [e.g. Mac OS] 26 | - Node version: [e.g. 18.16] 27 | - Browser [e.g. chrome, safari] 28 | - Package version [e.g. 1.0.0] 29 | - Build [e.g. umd, cjs] 30 | 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /test/common.test.ts: -------------------------------------------------------------------------------- 1 | import { DID } from '@iden3/js-iden3-core'; 2 | import { checkUserState } from '@lib/circuits/common'; 3 | import { mockResolverWithNoStateInContract, mockResolverWithNotLatestState } from './mocks'; 4 | import { Hash } from '@iden3/js-merkletree'; 5 | 6 | describe('Common', () => { 7 | const issuerDID = DID.parse('did:iden3:polygon:mumbai:x6suHR8HkEYczV9yVeAKKiXCZAd25P8WS6QvNhszk'); 8 | const issuerID = DID.idFromDID(issuerDID); 9 | const hash = Hash.fromBigInt( 10 | BigInt('13483594486393726782589954979757194488582220051583949915340451442108840786819') 11 | ); 12 | it('checkUserState fails', async () => { 13 | await expect(checkUserState(mockResolverWithNoStateInContract, issuerID, hash)).rejects.toThrow( 14 | 'State is not genesis and not registered in the smart contract' 15 | ); 16 | }); 17 | it('checkUserState', async () => { 18 | await checkUserState(mockResolverWithNotLatestState, issuerID, hash); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/schema.test.ts: -------------------------------------------------------------------------------- 1 | import { DocumentLoader, getDocumentLoader } from '@iden3/js-jsonld-merklization'; 2 | 3 | describe('schema loader', () => { 4 | it('schema http loader', async () => { 5 | const url = 6 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v3.json-ld'; 7 | 8 | const loader: DocumentLoader = getDocumentLoader(); 9 | const schemaResult = (await loader(url)).document; 10 | expect(schemaResult).not.toBeNull(); 11 | expect(schemaResult).toBeDefined(); 12 | }); 13 | 14 | it('schema ipfs loader', async () => { 15 | let connectionString = process.env.IPFS_URL; 16 | if (!connectionString) { 17 | connectionString = 'https://ipfs.io'; 18 | } 19 | const loader = getDocumentLoader({ 20 | ipfsGatewayURL: connectionString 21 | }); 22 | const schemaResult = (await loader('ipfs://Qmb1Q5jLETkUkhswCVX52ntTCNQnRm3NyyGf1NZG98u5cv')) 23 | .document; 24 | expect(schemaResult).not.toBeNull(); 25 | expect(schemaResult).toBeDefined(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2023 0kims Association. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/state/types/ethers-contracts/common.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { Listener } from '@ethersproject/providers'; 5 | import type { Event, EventFilter } from 'ethers'; 6 | 7 | export interface TypedEvent = any, TArgsObject = any> extends Event { 8 | args: TArgsArray & TArgsObject; 9 | } 10 | 11 | export interface TypedEventFilter<_TEvent extends TypedEvent> extends EventFilter {} 12 | 13 | export interface TypedListener { 14 | (...listenerArg: [...__TypechainArgsArray, TEvent]): void; 15 | } 16 | 17 | type __TypechainArgsArray = T extends TypedEvent ? U : never; 18 | 19 | export interface OnEvent { 20 | ( 21 | eventFilter: TypedEventFilter, 22 | listener: TypedListener 23 | ): TRes; 24 | (eventName: string, listener: Listener): TRes; 25 | } 26 | 27 | export type MinEthersFactory = { 28 | deploy(...a: ARGS[]): Promise; 29 | }; 30 | 31 | export type GetContractTypeFromFactory = F extends MinEthersFactory ? C : never; 32 | 33 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 34 | ? Parameters 35 | : never; 36 | 37 | export type PromiseOrValue = T | Promise; 38 | -------------------------------------------------------------------------------- /src/circuits/common.ts: -------------------------------------------------------------------------------- 1 | import { Id, DID } from '@iden3/js-iden3-core'; 2 | import { IStateResolver, ResolvedState, Resolvers } from '@lib/state/resolver'; 3 | import { Hash } from '@iden3/js-merkletree'; 4 | export const userStateError = new Error(`user state is not valid`); 5 | export const gistStateError = new Error(`gist state is not valid`); 6 | 7 | export async function checkUserState( 8 | resolver: IStateResolver, 9 | userId: Id, 10 | userState: Hash 11 | ): Promise { 12 | return await resolver.resolve(userId.bigInt(), userState.bigInt()); 13 | } 14 | 15 | export async function checkGlobalState( 16 | resolver: IStateResolver, 17 | state: Hash 18 | ): Promise { 19 | return await resolver.rootResolve(state.bigInt()); 20 | } 21 | 22 | export async function checkIssuerNonRevState( 23 | resolver: IStateResolver, 24 | issuerId: Id, 25 | issuerClaimNonRevState: Hash 26 | ): Promise { 27 | return await resolver.resolve(issuerId.bigInt(), issuerClaimNonRevState.bigInt()); 28 | } 29 | 30 | export function getResolverByID(resolvers: Resolvers, id: Id): IStateResolver { 31 | const userDID = DID.parseFromId(id); 32 | return getResolverByDID(resolvers, userDID); 33 | } 34 | 35 | export function getResolverByDID(resolvers: Resolvers, did: DID): IStateResolver { 36 | const { blockchain, networkId } = DID.decodePartsFromId(DID.idFromDID(did)); 37 | return resolvers[`${blockchain}:${networkId}`]; 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: RUN ES LINT ANS TESTS 2 | on: push 3 | jobs: 4 | build: 5 | timeout-minutes: 7 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v4 10 | 11 | - name: Setup Node.js 12 | uses: actions/setup-node@v4 13 | with: 14 | node-version: 'lts/*' 15 | 16 | - name: Cache node modules 17 | id: cache-npm 18 | uses: actions/cache@v4 19 | env: 20 | cache-name: cache-node-modules 21 | with: 22 | # npm cache files are stored in `~/.npm` on Linux/macOS 23 | path: ~/.npm 24 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} 25 | restore-keys: | 26 | ${{ runner.os }}-build-${{ env.cache-name }}- 27 | ${{ runner.os }}-build- 28 | ${{ runner.os }}- 29 | 30 | # if tests fail, this step may show what dependencies are changed. 31 | - if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }} 32 | name: List the state of node modules 33 | continue-on-error: true 34 | run: npm list 35 | 36 | - name: Install modules 37 | run: npm ci 38 | 39 | - name: Run Prettier 40 | run: npm run format 41 | 42 | - name: Run ESLint 43 | run: npm run lint 44 | 45 | - name: Run Build 46 | run: npm run build 47 | 48 | - name: Download regular circuits for CI 'latest.zip' from S3 49 | run: mkdir ./test/testdata && wget https://circuits.privado.id/latest.zip -P ./test/testdata 50 | 51 | - name: Unzip circuits to folder 52 | run: cd ./test/testdata && unzip latest.zip && cd - && pwd 53 | 54 | - name: Download authV3 circuits 55 | run: wget https://circuits.privado.id/trusted-setup-authV3-3.0.0.zip -P ./test/testdata 56 | 57 | - name: Unzip circuits to folder 58 | run: cd ./test/testdata && unzip trusted-setup-authV3-3.0.0.zip && cd - && pwd 59 | 60 | - name: Run Tests 61 | run: npm run test 62 | -------------------------------------------------------------------------------- /src/circuits/authV2.ts: -------------------------------------------------------------------------------- 1 | import { getDateFromUnixTimestamp } from '@iden3/js-iden3-core'; 2 | import { PubSignalsVerifier, VerifyOpts } from '@lib/circuits/registry'; 3 | import { IDOwnershipPubSignals } from '@lib/circuits/ownershipVerifier'; 4 | import { checkGlobalState, getResolverByID } from '@lib/circuits/common'; 5 | import { Resolvers } from '@lib/state/resolver'; 6 | import { AuthV2PubSignals, BaseConfig, byteEncoder } from '@0xpolygonid/js-sdk'; 7 | import { CONSTANTS } from '@lib/constants'; 8 | 9 | /** 10 | * @deprecated, use AuthPubSignals from './auth' instead 11 | */ 12 | export class AuthPubSignalsV2 extends IDOwnershipPubSignals implements PubSignalsVerifier { 13 | pubSignals = new AuthV2PubSignals(); 14 | constructor(pubSignals: string[]) { 15 | super(); 16 | this.pubSignals = this.pubSignals.pubSignalsUnmarshal( 17 | byteEncoder.encode(JSON.stringify(pubSignals)) 18 | ); 19 | 20 | this.userId = this.pubSignals.userID; 21 | this.challenge = this.pubSignals.challenge; 22 | } 23 | 24 | verifyQuery(): Promise { 25 | throw new Error(`authV2 circuit doesn't support queries`); 26 | } 27 | 28 | async verifyStates(resolvers: Resolvers, opts?: VerifyOpts): Promise { 29 | const resolver = getResolverByID(resolvers, this.userId); 30 | if (!resolver) { 31 | throw new Error(`resolver not found for id ${this.userId.string()}`); 32 | } 33 | const gist = await checkGlobalState(resolver, this.pubSignals.GISTRoot); 34 | 35 | const acceptedStateTransitionDelay = 36 | opts?.acceptedStateTransitionDelay ?? CONSTANTS.AUTH_ACCEPTED_STATE_TRANSITION_DELAY; 37 | 38 | if (!gist.latest) { 39 | const timeDiff = 40 | Date.now() - getDateFromUnixTimestamp(Number(gist.transitionTimestamp)).getTime(); 41 | if (timeDiff > acceptedStateTransitionDelay) { 42 | throw new Error('global state is outdated'); 43 | } 44 | } 45 | } 46 | 47 | verifyIdOwnership(sender: string, challenge: bigint): Promise { 48 | return super.verifyIdOwnership(sender, challenge); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/circuits/registry.ts: -------------------------------------------------------------------------------- 1 | import { AtomicQueryMTPV2PubSignalsVerifier } from '@lib/circuits/atomicMtpV2'; 2 | import { AtomicQuerySigV2PubSignalsVerifier } from '@lib/circuits/atomicSigV2'; 3 | import { Query } from '@lib/circuits/query'; 4 | import { Resolvers } from '@lib/state/resolver'; 5 | import { DocumentLoader } from '@iden3/js-jsonld-merklization'; 6 | import { AtomicQueryV3PubSignalsVerifier } from '@lib/circuits/atomicV3'; 7 | import { BaseConfig, VerifiablePresentation } from '@0xpolygonid/js-sdk'; 8 | import { LinkedMultiQueryVerifier } from '@lib/circuits/linkedMultiQuery'; 9 | import { AuthPubSignals } from './auth'; 10 | 11 | export type VerifyOpts = { 12 | // acceptedStateTransitionDelay is the period of time in milliseconds that a revoked state remains valid. 13 | acceptedStateTransitionDelay?: number; 14 | // acceptedProofGenerationDelay is the period of time in milliseconds that a generated proof remains valid. 15 | acceptedProofGenerationDelay?: number; 16 | // allowExpiredMessages is a flag that allows the verification of expired messages. 17 | allowExpiredMessages?: boolean; 18 | }; 19 | 20 | export interface PubSignalsVerifier { 21 | verifyQuery( 22 | query: Query, 23 | schemaLoader?: DocumentLoader, 24 | verifiablePresentation?: VerifiablePresentation, 25 | opts?: VerifyOpts, 26 | circuitParams?: { [key: string]: unknown } 27 | ): Promise; 28 | verifyStates(resolver: Resolvers, opts?: VerifyOpts): Promise; 29 | verifyIdOwnership(sender: string, challenge: bigint): Promise; 30 | } 31 | 32 | export interface PubSignals { 33 | new (pubSignals: string[]): PubSignalsVerifier; 34 | } 35 | 36 | const authV2 = AuthPubSignals; 37 | const authV3 = AuthPubSignals; 38 | const authV3_8_32 = AuthPubSignals; 39 | const credentialAtomicQueryMTPV2 = AtomicQueryMTPV2PubSignalsVerifier; 40 | const credentialAtomicQuerySigV2 = AtomicQuerySigV2PubSignalsVerifier; 41 | const credentialAtomicQueryV3 = AtomicQueryV3PubSignalsVerifier; 42 | const linkedMultiQuery10 = LinkedMultiQueryVerifier; 43 | 44 | export type VerifierType = PubSignalsVerifier & PubSignals; 45 | 46 | const supportedCircuits: { [key: string]: unknown } = { 47 | authV2, 48 | authV3, 49 | authV3_8_32, 50 | credentialAtomicQueryMTPV2, 51 | credentialAtomicQuerySigV2, 52 | credentialAtomicQueryV3, 53 | linkedMultiQuery10 54 | }; 55 | 56 | export class Circuits { 57 | static getCircuitPubSignals(id: string): VerifierType { 58 | id = id.split('-')[0]; 59 | return supportedCircuits[id] as VerifierType; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@iden3/js-iden3-auth", 3 | "version": "1.12.2", 4 | "description": "iden3-auth implementation in JavaScript", 5 | "main": "dist/node/cjs/index.js", 6 | "module": "dist/node/esm/index.js", 7 | "source": "./src/index.ts", 8 | "typings": "dist/types/index.d.ts", 9 | "files": [ 10 | "dist", 11 | "patches" 12 | ], 13 | "scripts": { 14 | "test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" npx jest", 15 | "test:watch": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" npx jest --watch", 16 | "build": "npm run clean && npm run build:cjs && npm run build:esm && npm run build:types", 17 | "build:esm": "tsc --outDir dist/node/esm && tsc-alias -p tsconfig.json --outDir dist/node/esm -f --resolve-full-extension .js", 18 | "build:cjs": "tsc --module commonjs --outDir dist/node/cjs && tsc-alias -p tsconfig.json --outDir dist/node/cjs", 19 | "build:types": "tsc -p tsconfig.json --declaration --emitDeclarationOnly --declarationDir dist/types && tsc-alias -p tsconfig.json --outDir dist/types", 20 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 21 | "lint": "eslint src test --ext .ts src/** test/*.ts", 22 | "lint:check": "eslint --ext .ts src/** test/*.ts", 23 | "clean": "rimraf ./dist" 24 | }, 25 | "directories": { 26 | "templates": "templates" 27 | }, 28 | "keywords": [ 29 | "iden3" 30 | ], 31 | "author": "iden3", 32 | "license": "MIT or Apache-2.0", 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/iden3/js-iden3-auth" 36 | }, 37 | "dependencies": { 38 | "@0xpolygonid/js-sdk": "1.38.0", 39 | "@iden3/js-crypto": "1.3.2", 40 | "@iden3/js-iden3-core": "1.8.0", 41 | "@iden3/js-jsonld-merklization": "1.7.2", 42 | "@iden3/js-jwz": "1.12.2", 43 | "@iden3/js-merkletree": "1.5.1", 44 | "did-resolver": "^4.1.0", 45 | "ethers": "^5.7.2", 46 | "tslib": "^2.6.2", 47 | "uuid": "^9.0.1" 48 | }, 49 | "devDependencies": { 50 | "@cspell/eslint-plugin": "^8.14.2", 51 | "@iden3/eslint-config": "https://github.com/iden3/eslint-config", 52 | "@typechain/ethers-v5": "^10.2.0", 53 | "@types/jest": "^29.5.2", 54 | "@types/jsonld": "^1.5.13", 55 | "@types/node": "^20.10.1", 56 | "@types/uuid": "^9.0.7", 57 | "@typescript-eslint/eslint-plugin": "^5.0.0", 58 | "@typescript-eslint/parser": "^5.0.0", 59 | "eslint": "^8.13.0", 60 | "eslint-config-prettier": "^8.3.0", 61 | "eslint-plugin-prettier": "^4.0.0", 62 | "jest": "29.1.0", 63 | "postinstall-postinstall": "^2.1.0", 64 | "prettier": "^2.7.1", 65 | "rimraf": "^3.0.2", 66 | "rollup-plugin-sourcemaps": "^0.6.3", 67 | "rollup-plugin-terser": "^7.0.2", 68 | "ts-jest": "29.1.1", 69 | "ts-node": "^10.9.1", 70 | "tsc-alias": "^1.8.8", 71 | "tsconfig-paths": "^3.14.2", 72 | "typechain": "^8.1.1", 73 | "typescript": "^4.8.4" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/circuits/atomicSigV2.ts: -------------------------------------------------------------------------------- 1 | import { PubSignalsVerifier, VerifyOpts } from '@lib/circuits/registry'; 2 | import { checkQueryV2Circuits, ClaimOutputs, Query } from '@lib/circuits/query'; 3 | import { Resolvers } from '@lib/state/resolver'; 4 | import { IDOwnershipPubSignals } from '@lib/circuits/ownershipVerifier'; 5 | import { checkIssuerNonRevState, checkUserState, getResolverByID } from '@lib/circuits/common'; 6 | import { getDateFromUnixTimestamp } from '@iden3/js-iden3-core'; 7 | import { DocumentLoader } from '@iden3/js-jsonld-merklization'; 8 | import { 9 | AtomicQuerySigV2PubSignals, 10 | BaseConfig, 11 | byteEncoder, 12 | CircuitId, 13 | VerifiablePresentation 14 | } from '@0xpolygonid/js-sdk'; 15 | import { CONSTANTS } from '@lib/constants'; 16 | 17 | export class AtomicQuerySigV2PubSignalsVerifier 18 | extends IDOwnershipPubSignals 19 | implements PubSignalsVerifier 20 | { 21 | pubSignals = new AtomicQuerySigV2PubSignals(); 22 | 23 | constructor(pubSignals: string[]) { 24 | super(); 25 | this.pubSignals = this.pubSignals.pubSignalsUnmarshal( 26 | byteEncoder.encode(JSON.stringify(pubSignals)) 27 | ); 28 | 29 | this.userId = this.pubSignals.userID; 30 | this.challenge = this.pubSignals.requestID; 31 | } 32 | 33 | async verifyQuery( 34 | query: Query, 35 | schemaLoader?: DocumentLoader, 36 | verifiablePresentation?: VerifiablePresentation, 37 | opts?: VerifyOpts 38 | ): Promise { 39 | const outs: ClaimOutputs = { 40 | issuerId: this.pubSignals.issuerID, 41 | schemaHash: this.pubSignals.claimSchema, 42 | slotIndex: this.pubSignals.slotIndex, 43 | operator: this.pubSignals.operator, 44 | value: this.pubSignals.value, 45 | timestamp: this.pubSignals.timestamp, 46 | merklized: this.pubSignals.merklized, 47 | claimPathKey: this.pubSignals.claimPathKey, 48 | claimPathNotExists: this.pubSignals.claimPathNotExists, 49 | valueArraySize: CONSTANTS.CIRCUITS_ARRAY_VALUE_SIZE, 50 | isRevocationChecked: this.pubSignals.isRevocationChecked 51 | }; 52 | 53 | await checkQueryV2Circuits( 54 | CircuitId.AtomicQuerySigV2, 55 | query, 56 | outs, 57 | schemaLoader, 58 | opts, 59 | verifiablePresentation 60 | ); 61 | 62 | return this.pubSignals; 63 | } 64 | async verifyStates(resolvers: Resolvers, opts?: VerifyOpts): Promise { 65 | const resolver = getResolverByID(resolvers, this.pubSignals.issuerID); 66 | if (!resolver) { 67 | throw new Error(`resolver not found for issuerID ${this.pubSignals.issuerID.string()}`); 68 | } 69 | 70 | await checkUserState(resolver, this.pubSignals.issuerID, this.pubSignals.issuerAuthState); 71 | 72 | if (this.pubSignals.isRevocationChecked === 0) { 73 | return; 74 | } 75 | 76 | const issuerNonRevStateResolved = await checkIssuerNonRevState( 77 | resolver, 78 | this.pubSignals.issuerID, 79 | this.pubSignals.issuerClaimNonRevState 80 | ); 81 | 82 | const acceptedStateTransitionDelay = 83 | opts?.acceptedStateTransitionDelay ?? CONSTANTS.ACCEPTED_STATE_TRANSITION_DELAY; 84 | 85 | if (!issuerNonRevStateResolved.latest) { 86 | const timeDiff = 87 | Date.now() - 88 | getDateFromUnixTimestamp(Number(issuerNonRevStateResolved.transitionTimestamp)).getTime(); 89 | if (timeDiff > acceptedStateTransitionDelay) { 90 | throw new Error('issuer state is outdated'); 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/circuits/query.ts: -------------------------------------------------------------------------------- 1 | import { Id, SchemaHash } from '@iden3/js-iden3-core'; 2 | import { getDocumentLoader } from '@iden3/js-jsonld-merklization'; 3 | import { DocumentLoader } from '@iden3/js-jsonld-merklization'; 4 | import { 5 | Operators, 6 | CircuitId, 7 | ProofQuery, 8 | parseQueriesMetadata, 9 | checkQueryRequest, 10 | validateDisclosureV2Circuit, 11 | validateEmptyCredentialSubjectV2Circuit, 12 | verifyFieldValueInclusionV2, 13 | validateOperators, 14 | checkCircuitOperator, 15 | JsonDocumentObject, 16 | VerifiablePresentation 17 | } from '@0xpolygonid/js-sdk'; 18 | import { VerifyOpts } from './registry'; 19 | import { JsonLd } from 'jsonld/jsonld-spec'; 20 | 21 | export const userStateError = new Error(`user state is not valid`); 22 | 23 | // Query is a query to circuit 24 | export interface Query { 25 | allowedIssuers: string[]; 26 | credentialSubject: JsonDocumentObject; 27 | context: string; 28 | type: string; 29 | claimID?: string; 30 | skipClaimRevocationCheck?: boolean; 31 | proofType?: string; 32 | groupId?: number; 33 | } 34 | 35 | // ClaimOutputs fields that are used in proof generation 36 | export interface ClaimOutputs { 37 | issuerId: Id; 38 | schemaHash: SchemaHash; 39 | slotIndex?: number; 40 | operator: number; 41 | value: bigint[]; 42 | timestamp: number; 43 | merklized: number; 44 | claimPathKey?: bigint; 45 | claimPathNotExists?: number; 46 | valueArraySize: number; 47 | isRevocationChecked: number; 48 | operatorOutput?: bigint; 49 | } 50 | 51 | export async function checkQueryV2Circuits( 52 | circuitId: CircuitId.AtomicQueryMTPV2 | CircuitId.AtomicQuerySigV2, 53 | query: ProofQuery, 54 | outs: ClaimOutputs, 55 | schemaLoader: DocumentLoader | undefined, 56 | opts: VerifyOpts | undefined, 57 | verifiablePresentation: VerifiablePresentation | undefined 58 | ) { 59 | if (!query.type) { 60 | throw new Error(`proof query type is undefined`); 61 | } 62 | 63 | const loader = schemaLoader || getDocumentLoader(); 64 | 65 | // validate schema 66 | let context: JsonLd; 67 | try { 68 | context = (await loader(query.context ?? '')).document; 69 | } catch (e) { 70 | throw new Error(`can't load schema for request query`); 71 | } 72 | 73 | const queriesMetadata = await parseQueriesMetadata( 74 | query.type, 75 | JSON.stringify(context), 76 | query.credentialSubject as JsonDocumentObject, 77 | { 78 | documentLoader: loader 79 | } 80 | ); 81 | 82 | await checkQueryRequest(query, queriesMetadata, context, outs, circuitId, loader, opts); 83 | 84 | const queryMetadata = queriesMetadata[0]; // only one query is supported 85 | 86 | checkCircuitOperator(circuitId, outs.operator); 87 | 88 | // validate selective disclosure 89 | if (queryMetadata.operator === Operators.SD) { 90 | try { 91 | await validateDisclosureV2Circuit(queryMetadata, outs, verifiablePresentation, loader); 92 | } catch (e) { 93 | throw new Error(`failed to validate selective disclosure: ${(e as Error).message}`); 94 | } 95 | } else if (!queryMetadata.fieldName && queryMetadata.operator == Operators.NOOP) { 96 | try { 97 | await validateEmptyCredentialSubjectV2Circuit(queryMetadata, outs); 98 | } catch (e: unknown) { 99 | throw new Error(`failed to validate operators: ${(e as Error).message}`); 100 | } 101 | } else { 102 | try { 103 | await validateOperators(queryMetadata, outs); 104 | } catch (e) { 105 | throw new Error(`failed to validate operators: ${(e as Error).message}`); 106 | } 107 | } 108 | 109 | // verify field inclusion 110 | verifyFieldValueInclusionV2(outs, queryMetadata); 111 | } 112 | -------------------------------------------------------------------------------- /src/circuits/atomicMtpV2.ts: -------------------------------------------------------------------------------- 1 | import { getDateFromUnixTimestamp } from '@iden3/js-iden3-core'; 2 | import { Resolvers } from '@lib/state/resolver'; 3 | import { checkQueryV2Circuits, ClaimOutputs, Query } from '@lib/circuits/query'; 4 | import { PubSignalsVerifier, VerifyOpts } from '@lib/circuits/registry'; 5 | import { IDOwnershipPubSignals } from '@lib/circuits/ownershipVerifier'; 6 | import { checkIssuerNonRevState, checkUserState, getResolverByID } from '@lib/circuits/common'; 7 | import { DocumentLoader } from '@iden3/js-jsonld-merklization'; 8 | import { 9 | AtomicQueryMTPV2PubSignals, 10 | BaseConfig, 11 | byteEncoder, 12 | CircuitId, 13 | VerifiablePresentation 14 | } from '@0xpolygonid/js-sdk'; 15 | import { CONSTANTS } from '@lib/constants'; 16 | 17 | export class AtomicQueryMTPV2PubSignalsVerifier 18 | extends IDOwnershipPubSignals 19 | implements PubSignalsVerifier 20 | { 21 | pubSignals = new AtomicQueryMTPV2PubSignals(); 22 | constructor(pubSignals: string[]) { 23 | super(); 24 | this.pubSignals = this.pubSignals.pubSignalsUnmarshal( 25 | byteEncoder.encode(JSON.stringify(pubSignals)) 26 | ); 27 | 28 | if (!this.pubSignals.userID) { 29 | throw new Error('user id is not presented in proof public signals'); 30 | } 31 | 32 | if (!this.pubSignals.requestID) { 33 | throw new Error('requestId is not presented in proof public signals'); 34 | } 35 | 36 | this.userId = this.pubSignals.userID; 37 | this.challenge = this.pubSignals.requestID; 38 | } 39 | 40 | async verifyQuery( 41 | query: Query, 42 | schemaLoader?: DocumentLoader, 43 | verifiablePresentation?: VerifiablePresentation, 44 | opts?: VerifyOpts 45 | ): Promise { 46 | const outs: ClaimOutputs = { 47 | issuerId: this.pubSignals.issuerID, 48 | schemaHash: this.pubSignals.claimSchema, 49 | slotIndex: this.pubSignals.slotIndex, 50 | operator: this.pubSignals.operator, 51 | value: this.pubSignals.value, 52 | timestamp: this.pubSignals.timestamp, 53 | merklized: this.pubSignals.merklized, 54 | claimPathKey: this.pubSignals.claimPathKey, 55 | claimPathNotExists: this.pubSignals.claimPathNotExists, 56 | valueArraySize: CONSTANTS.CIRCUITS_ARRAY_VALUE_SIZE, 57 | isRevocationChecked: this.pubSignals.isRevocationChecked 58 | }; 59 | await checkQueryV2Circuits( 60 | CircuitId.AtomicQueryMTPV2, 61 | query, 62 | outs, 63 | schemaLoader, 64 | opts, 65 | verifiablePresentation 66 | ); 67 | 68 | return this.pubSignals; 69 | } 70 | 71 | async verifyStates(resolvers: Resolvers, opts?: VerifyOpts): Promise { 72 | const resolver = getResolverByID(resolvers, this.pubSignals.issuerID); 73 | if (!resolver) { 74 | throw new Error(`resolver not found for issuerID ${this.pubSignals.issuerID.string()}`); 75 | } 76 | 77 | await checkUserState(resolver, this.pubSignals.issuerID, this.pubSignals.issuerClaimIdenState); 78 | 79 | if (this.pubSignals.isRevocationChecked === 0) { 80 | return; 81 | } 82 | 83 | const issuerNonRevStateResolved = await checkIssuerNonRevState( 84 | resolver, 85 | this.pubSignals.issuerID, 86 | this.pubSignals.issuerClaimNonRevState 87 | ); 88 | 89 | const acceptedStateTransitionDelay = 90 | opts?.acceptedStateTransitionDelay ?? CONSTANTS.ACCEPTED_STATE_TRANSITION_DELAY; 91 | 92 | if (!issuerNonRevStateResolved.latest) { 93 | const timeDiff = 94 | Date.now() - 95 | getDateFromUnixTimestamp(Number(issuerNonRevStateResolved.transitionTimestamp)).getTime(); 96 | if (timeDiff > acceptedStateTransitionDelay) { 97 | throw new Error('issuer state is outdated'); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /test/mocks.ts: -------------------------------------------------------------------------------- 1 | import { cacheLoader } from '@0xpolygonid/js-sdk'; 2 | import { DocumentLoader } from '@iden3/js-jsonld-merklization'; 3 | import { VerifyOpts } from '@lib/circuits'; 4 | import { IStateResolver, ResolvedState, Resolvers } from '@lib/state/resolver'; 5 | import { DIDResolutionResult } from 'did-resolver'; 6 | import { fileURLToPath } from 'url'; 7 | import { dirname, join } from 'path'; 8 | 9 | // ES Module equivalent of __dirname 10 | export function getTestDataPath(relativePath: string): string { 11 | const __filename = fileURLToPath(import.meta.url); 12 | const __dirname = dirname(__filename); 13 | return join(__dirname, relativePath); 14 | } 15 | 16 | class MockResolver implements IStateResolver { 17 | resolve(): Promise { 18 | const t: ResolvedState = { 19 | latest: true, 20 | state: null, 21 | genesis: false, 22 | transitionTimestamp: 0 23 | }; 24 | return Promise.resolve(t); 25 | } 26 | rootResolve(): Promise { 27 | const t: ResolvedState = { 28 | latest: true, 29 | state: null, 30 | genesis: false, 31 | transitionTimestamp: 0 32 | }; 33 | return Promise.resolve(t); 34 | } 35 | } 36 | 37 | class MockResolverWithNoStateInContract implements IStateResolver { 38 | resolve(): Promise { 39 | throw new Error('State is not genesis and not registered in the smart contract'); 40 | } 41 | rootResolve(): Promise { 42 | throw new Error('GIST root does not exist in the smart contract'); 43 | } 44 | } 45 | 46 | class MockResolverWithNotLatestState implements IStateResolver { 47 | resolve(): Promise { 48 | const t: ResolvedState = { 49 | latest: false, 50 | state: null, 51 | genesis: false, 52 | transitionTimestamp: 1712653265 53 | }; 54 | return Promise.resolve(t); 55 | } 56 | rootResolve(): Promise { 57 | const t: ResolvedState = { 58 | latest: false, 59 | state: null, 60 | genesis: false, 61 | transitionTimestamp: 1712653265 62 | }; 63 | return Promise.resolve(t); 64 | } 65 | } 66 | 67 | export const exampleDidDoc = { 68 | '@context': [ 69 | 'https://www.w3.org/ns/did/v1', 70 | { 71 | EcdsaSecp256k1RecoveryMethod2020: 72 | 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020#EcdsaSecp256k1RecoveryMethod2020', 73 | blockchainAccountId: 'https://w3id.org/security#blockchainAccountId' 74 | } 75 | ], 76 | id: 'did:pkh:poly:0x7141E4d20F7644DC8c0AdCA8a520EC83C6cABD65', 77 | verificationMethod: [ 78 | { 79 | id: 'did:pkh:poly:0x7141E4d20F7644DC8c0AdCA8a520EC83C6cABD65#Recovery2020', 80 | type: 'EcdsaSecp256k1RecoveryMethod2020', 81 | controller: 'did:pkh:poly:0x7141E4d20F7644DC8c0AdCA8a520EC83C6cABD65', 82 | blockchainAccountId: 'eip155:137:0x7141E4d20F7644DC8c0AdCA8a520EC83C6cABD65' 83 | } 84 | ], 85 | authentication: ['did:pkh:poly:0x7141E4d20F7644DC8c0AdCA8a520EC83C6cABD65#Recovery2020'], 86 | assertionMethod: ['did:pkh:poly:0x7141E4d20F7644DC8c0AdCA8a520EC83C6cABD65#Recovery2020'] 87 | }; 88 | 89 | export const testOpts: VerifyOpts = { 90 | acceptedStateTransitionDelay: 5 * 60 * 1000, // 5 minutes 91 | acceptedProofGenerationDelay: 10 * 365 * 24 * 60 * 60 * 1000 // 10 years 92 | }; 93 | 94 | const mockStateResolver: MockResolver = new MockResolver(); 95 | export const mockResolverWithNoStateInContract: MockResolver = 96 | new MockResolverWithNoStateInContract(); 97 | export const mockResolverWithNotLatestState: MockResolver = new MockResolverWithNotLatestState(); 98 | export const resolvers: Resolvers = { 99 | 'polygon:amoy': mockStateResolver, 100 | 'polygon:mumbai': mockStateResolver 101 | }; 102 | export const resolveDIDDocument = { 103 | resolve: () => Promise.resolve({ didDocument: exampleDidDoc } as DIDResolutionResult) 104 | }; 105 | 106 | export const schemaLoader: DocumentLoader = cacheLoader({ 107 | ipfsNodeURL: process.env.IPFS_URL ?? 'https://ipfs.io' 108 | }); 109 | -------------------------------------------------------------------------------- /src/circuits/linkedMultiQuery.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import { PubSignalsVerifier } from '@lib/circuits/registry'; 3 | import { Query } from '@lib/circuits/query'; 4 | import { DocumentLoader, Path } from '@iden3/js-jsonld-merklization'; 5 | import { 6 | BaseConfig, 7 | JSONObject, 8 | LinkedMultiQueryPubSignals, 9 | byteEncoder, 10 | cacheLoader, 11 | parseQueriesMetadata, 12 | calculateQueryHashV3, 13 | calculateCoreSchemaHash, 14 | QueryMetadata, 15 | LinkedMultiQueryInputs, 16 | Operators, 17 | fieldValueFromVerifiablePresentation, 18 | VerifiablePresentation 19 | } from '@0xpolygonid/js-sdk'; 20 | 21 | /** 22 | * Verifies the linked multi-query circuit. 23 | * @beta 24 | */ 25 | export class LinkedMultiQueryVerifier implements PubSignalsVerifier { 26 | readonly pubSignals = new LinkedMultiQueryPubSignals(); 27 | 28 | constructor(pubSignals: string[]) { 29 | this.pubSignals = this.pubSignals.pubSignalsUnmarshal( 30 | byteEncoder.encode(JSON.stringify(pubSignals)) 31 | ); 32 | } 33 | 34 | verifyIdOwnership(): Promise { 35 | return Promise.resolve(); 36 | } 37 | 38 | async verifyQuery( 39 | query: Query, 40 | schemaLoader?: DocumentLoader, 41 | verifiablePresentation?: VerifiablePresentation 42 | ): Promise { 43 | let schema: JSONObject; 44 | const ldOpts = { documentLoader: schemaLoader ?? cacheLoader() }; 45 | try { 46 | schema = (await ldOpts.documentLoader(query.context)).document as JSONObject; 47 | } catch (e) { 48 | throw new Error(`can't load schema for request query`); 49 | } 50 | const ldContextJSON = JSON.stringify(schema); 51 | const credentialSubject = query.credentialSubject; 52 | const schemaId: string = await Path.getTypeIDFromContext(ldContextJSON, query.type, ldOpts); 53 | const schemaHash = calculateCoreSchemaHash(byteEncoder.encode(schemaId)); 54 | 55 | const queriesMetadata = await parseQueriesMetadata( 56 | query.type, 57 | ldContextJSON, 58 | credentialSubject, 59 | ldOpts 60 | ); 61 | 62 | const request: { queryHash: bigint; queryMeta: QueryMetadata }[] = []; 63 | const merklized = queriesMetadata[0]?.merklizedSchema ? 1 : 0; 64 | for (let i = 0; i < LinkedMultiQueryInputs.queryCount; i++) { 65 | const queryMeta = queriesMetadata[i]; 66 | const values = queryMeta?.values ?? []; 67 | const valArrSize = values.length; 68 | 69 | const queryHash = calculateQueryHashV3( 70 | values, 71 | schemaHash, 72 | queryMeta?.slotIndex ?? 0, 73 | queryMeta?.operator ?? 0, 74 | queryMeta?.claimPathKey.toString() ?? 0, 75 | valArrSize, 76 | merklized, 77 | 0, 78 | 0, 79 | 0 80 | ); 81 | request.push({ queryHash, queryMeta }); 82 | } 83 | 84 | const queryHashCompare = (a: { queryHash: bigint }, b: { queryHash: bigint }): number => { 85 | if (a.queryHash < b.queryHash) return -1; 86 | if (a.queryHash > b.queryHash) return 1; 87 | return 0; 88 | }; 89 | 90 | const pubSignalsMeta = this.pubSignals.circuitQueryHash.map((queryHash, index) => ({ 91 | queryHash, 92 | operatorOutput: this.pubSignals.operatorOutput[index] 93 | })); 94 | 95 | pubSignalsMeta.sort(queryHashCompare); 96 | request.sort(queryHashCompare); 97 | 98 | for (let i = 0; i < LinkedMultiQueryInputs.queryCount; i++) { 99 | if (request[i].queryHash != pubSignalsMeta[i].queryHash) { 100 | throw new Error('query hashes do not match'); 101 | } 102 | 103 | if (request[i].queryMeta?.operator === Operators.SD) { 104 | const disclosedValue = await fieldValueFromVerifiablePresentation( 105 | request[i].queryMeta.fieldName, 106 | verifiablePresentation, 107 | schemaLoader 108 | ); 109 | if (disclosedValue != pubSignalsMeta[i].operatorOutput) { 110 | throw new Error('disclosed value is not in the proof outputs'); 111 | } 112 | } 113 | } 114 | 115 | return this.pubSignals as unknown as BaseConfig; 116 | } 117 | 118 | async verifyStates(): Promise { 119 | return Promise.resolve(); 120 | } 121 | 122 | private bigIntCompare = (a: bigint, b: bigint): number => { 123 | if (a < b) return -1; 124 | if (a > b) return 1; 125 | return 0; 126 | }; 127 | } 128 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # js-iden3-auth 2 | 3 | > Library for verification of authorization response messages of communication protocol in JWZ format 4 | > 5 | 6 | `npm i @iden3/js-iden3-auth --save` 7 | 8 | ### General description 9 | 10 | The goal of iden3auth libraries is to handle authentication messages of communication protocol. 11 | 12 | Currently, library implementation includes support of next message types 13 | 14 | 1. `https://iden3-communication.io/authorization/1.0/request` 15 | 2. `https://iden3-communication.io/authorization/1.0/response` 16 | 17 | ### RUN AND TEST 18 | 19 | 1. Download regular circuits from S3 and unzip circuits to folder 20 | ``` 21 | mkdir ./test/testdata && wget https://circuits.privado.id/latest.zip -P ./test/testdata 22 | 23 | cd ./test/testdata && unzip latest.zip && cd - && pwd 24 | ``` 25 | 2. Download authV3 circuits and unzip circuits to folder 26 | ``` 27 | wget https://circuits.privado.id/trusted-setup-authV3-3.0.0.zip -P ./test/testdata 28 | 29 | cd ./test/testdata && unzip trusted-setup-authV3-3.0.0.zip && cd - && pwd 30 | ``` 31 | 3. Run tests 32 | ``` 33 | npm run test 34 | ``` 35 | 36 | **Temporal:** 37 | For now to run jest tests without experimental feature support: 38 | 39 | 1. add mocked folder 40 | 41 | 2. change jest config. 42 | 43 | 44 | --- 45 | 46 | Auth verification procedure: 47 | 48 | 1. JWZ token verification 49 | 2. Zero-knowledge proof verification of request proofs 50 | 3. Query request verification for atomic circuits 51 | 4. Verification of identity and issuer states for atomic circuits 52 | 53 | ### Zero-knowledge proof verification 54 | 55 | > Groth16 proof are supported by auth library 56 | > 57 | 58 | Verification keys must be provided using `IKeyLoader` interface 59 | 60 | ### Query verification 61 | 62 | Proof for each atomic circuit contains public signals that allow extracting user and issuer identifiers, states, signature challenges, etc. 63 | Circuit public signals marshallers are defined inside library.To use custom circuit you need to register it with `registerCircuitPubSignals` function. 64 | 65 | ### Verification of user / issuer identity states 66 | 67 | The blockchain verification algorithm is used 68 | 69 | 1. Gets state from the blockchain (address of id state contract and URL must be provided by the caller of the library): 70 | 1. Empty state is returned - it means that identity state hasn’t been updated or updated state hasn’t been published. We need to compare id and state. If they are different it’s not a genesis state of identity then it’s not valid. 71 | 2. The non-empty state is returned and equals to the state in provided proof which means that the user state is fresh enough and we work with the latest user state. 72 | 3. The non-empty state is returned and it’s not equal to the state that the user has provided. Gets the transition time of the state. The verification party can make a decision if it can accept this state based on that time frame. 73 | 74 | 2. Only latest states for user are valid. Any existing issuer state for claim issuance is valid. 75 | 76 | ### Verification of GIST 77 | 78 | The blockchain verification algorithm is used 79 | 80 | 1. Get GIST from the blockchain (address of id state contract and URL must be provided by the caller of the library): 81 | 1. A non-empty GIST is returned, equal to the GIST is provided by the user, it means the user is using the latest state. 82 | 2. The non-empty GIST is returned and it’s not equal to the GIST is provided by a user. Gets the transition time of the GIST. The verification party can make a decision if it can accept this state based on that time frame. 83 | 84 | ## How to use 85 | 86 | 1. Import dependencies 87 | 88 | ``` javascript 89 | import { 90 | auth, 91 | resolver, 92 | protocol, 93 | loaders, 94 | circuits, 95 | } from 'js-iden3-auth'; 96 | ``` 97 | 98 | 2. Request generation: 99 | 100 | basic auth: 101 | 102 | ``` javascript 103 | const request = auth.createAuthorizationRequestWithMessage( 104 | 'test flow', // reason 105 | 'message to sign', // message 106 | '1125GJqgw6YEsKFwj63GY87MMxPL9kwDKxPUiwMLNZ', // sender 107 | 'http://example.com/callback?sessionId=1', // callback url 108 | ); 109 | ``` 110 | 111 | if you want request specific proof (example): 112 | 113 | ``` javascript 114 | const proofRequest: protocol.ZeroKnowledgeProofRequest = { 115 | id: 1, 116 | circuitId: 'credentialAtomicQueryMTPV2', 117 | query: { 118 | allowedIssuers: ['*'], 119 | type: 'KYCCountryOfResidenceCredential', 120 | context: 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v2.json-ld', 121 | credentialSubject: { 122 | countryCode: { 123 | $nin: [840, 120, 340, 509], 124 | }, 125 | }, 126 | }, 127 | }; 128 | request.body.scope = [...scope, proofRequest]; 129 | ``` 130 | 131 | 3. Token verification 132 | 133 | Init Verifier: 134 | 135 | ``` javascript 136 | const ethStateResolver = new resolver.EthStateResolver( 137 | ethUrl, 138 | contractAddress, 139 | ); 140 | 141 | const resolvers: resolver.Resolvers = { 142 | ['polygon:mumbai']: ethStateResolver, 143 | }; 144 | 145 | const schemaLoader = getDocumentLoader({ 146 | ipfsNodeURL: 'ipfs.io' 147 | }); 148 | const ethStateResolver = new resolver.EthStateResolver('rpc url', 'contractAddress'); 149 | const verifier = await auth.Verifier.newVerifier({ 150 | stateResolver: resolvers, 151 | circuitsDir: path.join(__dirname, './testdata'), 152 | documentLoader: schemaLoader 153 | } 154 | ); 155 | ``` 156 | 157 | FullVerify 158 | 159 | ``` javascript 160 | let authResponse: protocol.AuthorizationResponseMessage; 161 | authResponse = await verifier.fullVerify(tokenStr, authRequest); 162 | ``` 163 | 164 | Verify manually or thread id is used a session id to match request 165 | 166 | ``` javascript 167 | const token = await verifier.verifyJWZ(tokenStr); 168 | authResponse = JSON.parse( 169 | token.getPayload(), 170 | ) as protocol.AuthorizationResponseMessage; 171 | const authRequest: protocol.AuthorizationRequestMessage; // get request from you session storage. You can use authResponse.thid field 172 | 173 | await verifier.verifyAuthResponse(authResponse, authRequest); 174 | ``` 175 | 176 | --- 177 | 178 | ### Generate types for state contract 179 | 180 | We can use [TypeChain](https://github.com/dethcrypto/TypeChain#readme) for generate TS types for a smart contract. 181 | 182 | 1. Install TypeChain; 183 | 2. Install @typechain/ethers-v5; 184 | 3. Run: 185 | 186 | ```bash 187 | typechain --target ethers-v5 /path/to/state_contract.sol 188 | ``` 189 | ## License 190 | 191 | js-iden3-auth is part of the iden3 project copyright 2024 0kims association 192 | 193 | This project is licensed under either of 194 | 195 | - [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) ([`LICENSE-APACHE`](LICENSE-APACHE)) 196 | - [MIT license](https://opensource.org/licenses/MIT) ([`LICENSE-MIT`](LICENSE-MIT)) 197 | 198 | at your option. 199 | -------------------------------------------------------------------------------- /src/circuits/atomicV3.ts: -------------------------------------------------------------------------------- 1 | import { PubSignalsVerifier, VerifyOpts } from '@lib/circuits/registry'; 2 | import { ClaimOutputs, Query } from '@lib/circuits/query'; 3 | import { Resolvers } from '@lib/state/resolver'; 4 | import { IDOwnershipPubSignals } from '@lib/circuits/ownershipVerifier'; 5 | import { checkIssuerNonRevState, checkUserState, getResolverByID } from '@lib/circuits/common'; 6 | import { DID, getDateFromUnixTimestamp } from '@iden3/js-iden3-core'; 7 | import { DocumentLoader, getDocumentLoader } from '@iden3/js-jsonld-merklization'; 8 | import { 9 | AtomicQueryV3PubSignals, 10 | BaseConfig, 11 | byteEncoder, 12 | checkCircuitOperator, 13 | checkQueryRequest, 14 | CircuitId, 15 | JSONObject, 16 | Operators, 17 | parseQueriesMetadata, 18 | ProofType, 19 | validateDisclosureNativeSDSupport, 20 | validateEmptyCredentialSubjectNoopNativeSupport, 21 | validateOperators, 22 | VerifiablePresentation, 23 | verifyFieldValueInclusionNativeExistsSupport 24 | } from '@0xpolygonid/js-sdk'; 25 | import { JsonLd } from 'jsonld/jsonld-spec'; 26 | import { CONSTANTS } from '@lib/constants'; 27 | 28 | /** 29 | * Verifies the public signals for the AtomicQueryV3 circuit. 30 | * @beta 31 | */ 32 | export class AtomicQueryV3PubSignalsVerifier 33 | extends IDOwnershipPubSignals 34 | implements PubSignalsVerifier 35 | { 36 | pubSignals = new AtomicQueryV3PubSignals(); 37 | 38 | constructor(pubSignals: string[]) { 39 | super(); 40 | this.pubSignals = this.pubSignals.pubSignalsUnmarshal( 41 | byteEncoder.encode(JSON.stringify(pubSignals)) 42 | ); 43 | 44 | this.userId = this.pubSignals.userID; 45 | this.challenge = this.pubSignals.requestID; 46 | } 47 | 48 | async verifyQuery( 49 | query: Query, 50 | schemaLoader?: DocumentLoader, 51 | verifiablePresentation?: VerifiablePresentation, 52 | opts?: VerifyOpts, 53 | params?: JSONObject 54 | ): Promise { 55 | const outs: ClaimOutputs = { 56 | issuerId: this.pubSignals.issuerID, 57 | schemaHash: this.pubSignals.claimSchema, 58 | slotIndex: this.pubSignals.slotIndex, 59 | operator: this.pubSignals.operator, 60 | value: this.pubSignals.value, 61 | timestamp: this.pubSignals.timestamp, 62 | merklized: this.pubSignals.merklized, 63 | claimPathKey: this.pubSignals.claimPathKey, 64 | valueArraySize: CONSTANTS.CIRCUITS_ARRAY_VALUE_SIZE, 65 | isRevocationChecked: this.pubSignals.isRevocationChecked, 66 | operatorOutput: this.pubSignals.operatorOutput 67 | }; 68 | 69 | if (!query.type) { 70 | throw new Error(`proof query type is undefined`); 71 | } 72 | 73 | const loader = schemaLoader ?? getDocumentLoader(); 74 | 75 | // validate schema 76 | let context: JsonLd; 77 | try { 78 | context = (await loader(query.context ?? '')).document; 79 | } catch (e) { 80 | throw new Error(`can't load schema for request query`); 81 | } 82 | 83 | const queriesMetadata = await parseQueriesMetadata( 84 | query.type, 85 | JSON.stringify(context), 86 | query.credentialSubject, 87 | { 88 | documentLoader: loader 89 | } 90 | ); 91 | 92 | const circuitId = CircuitId.AtomicQueryV3; 93 | await checkQueryRequest( 94 | query, 95 | queriesMetadata, 96 | context, 97 | outs, 98 | CircuitId.AtomicQueryV3, 99 | loader, 100 | opts 101 | ); 102 | 103 | const queryMetadata = queriesMetadata[0]; // only one query is supported 104 | 105 | checkCircuitOperator(circuitId, outs.operator); 106 | // validate selective disclosure 107 | if (queryMetadata.operator === Operators.SD) { 108 | try { 109 | await validateDisclosureNativeSDSupport( 110 | queryMetadata, 111 | outs, 112 | verifiablePresentation, 113 | loader 114 | ); 115 | } catch (e) { 116 | throw new Error(`failed to validate selective disclosure: ${(e as Error).message}`); 117 | } 118 | } else if (!queryMetadata.fieldName && queryMetadata.operator == Operators.NOOP) { 119 | try { 120 | await validateEmptyCredentialSubjectNoopNativeSupport(outs); 121 | } catch (e: unknown) { 122 | throw new Error(`failed to validate operators: ${(e as Error).message}`); 123 | } 124 | } else { 125 | try { 126 | await validateOperators(queryMetadata, outs); 127 | } catch (e) { 128 | throw new Error(`failed to validate operators: ${(e as Error).message}`); 129 | } 130 | } 131 | 132 | // verify field inclusion / non-inclusion 133 | 134 | verifyFieldValueInclusionNativeExistsSupport(outs, queryMetadata); 135 | 136 | const { proofType, verifierID, nullifier, nullifierSessionID, linkID } = this.pubSignals; 137 | 138 | switch (query.proofType) { 139 | case ProofType.BJJSignature: 140 | if (proofType !== 1) { 141 | throw new Error('wrong proof type for BJJSignature'); 142 | } 143 | break; 144 | case ProofType.Iden3SparseMerkleTreeProof: 145 | if (proofType !== 2) { 146 | throw new Error('wrong proof type for Iden3SparseMerkleTreeProof'); 147 | } 148 | break; 149 | default: 150 | // if proof type is not specified in query any proof type in signals is OK. 151 | } 152 | 153 | const nSessionId = BigInt((params?.nullifierSessionId as string) ?? 0); 154 | if (nSessionId !== 0n) { 155 | if (BigInt(nullifier ?? 0) === 0n) { 156 | throw new Error('nullifier should be provided for nullification and should not be 0'); 157 | } 158 | // verify nullifier information 159 | const verifierDIDParam = params?.verifierDid; 160 | if (!verifierDIDParam) { 161 | throw new Error('verifierDid is required'); 162 | } 163 | 164 | const id = DID.idFromDID(verifierDIDParam as DID); 165 | 166 | if (verifierID.bigInt() != id.bigInt()) { 167 | throw new Error('wrong verifier is used for nullification'); 168 | } 169 | 170 | if (nullifierSessionID !== nSessionId) { 171 | throw new Error( 172 | `wrong verifier session id is used for nullification, expected ${nSessionId}, got ${nullifierSessionID}` 173 | ); 174 | } 175 | } else if (nullifierSessionID !== 0n) { 176 | throw new Error(`Nullifier id is generated but wasn't requested`); 177 | } 178 | 179 | if (!query.groupId && linkID !== 0n) { 180 | throw new Error(`proof contains link id, but group id is not provided`); 181 | } 182 | 183 | if (query.groupId && linkID === 0n) { 184 | throw new Error("proof doesn't contain link id, but group id is provided"); 185 | } 186 | 187 | return this.pubSignals; 188 | } 189 | 190 | async verifyStates(resolvers: Resolvers, opts?: VerifyOpts): Promise { 191 | const resolver = getResolverByID(resolvers, this.pubSignals.issuerID); 192 | if (!resolver) { 193 | throw new Error(`resolver not found for issuerID ${this.pubSignals.issuerID.string()}`); 194 | } 195 | 196 | await checkUserState(resolver, this.pubSignals.issuerID, this.pubSignals.issuerState); 197 | 198 | if (this.pubSignals.isRevocationChecked === 0) { 199 | return; 200 | } 201 | 202 | const issuerNonRevStateResolved = await checkIssuerNonRevState( 203 | resolver, 204 | this.pubSignals.issuerID, 205 | this.pubSignals.issuerClaimNonRevState 206 | ); 207 | 208 | const acceptedStateTransitionDelay = 209 | opts?.acceptedStateTransitionDelay ?? CONSTANTS.ACCEPTED_STATE_TRANSITION_DELAY; 210 | 211 | if (issuerNonRevStateResolved.latest) { 212 | return; 213 | } 214 | 215 | const timeDiff = 216 | Date.now() - 217 | getDateFromUnixTimestamp(Number(issuerNonRevStateResolved.transitionTimestamp)).getTime(); 218 | if (timeDiff > acceptedStateTransitionDelay) { 219 | throw new Error('issuer state is outdated'); 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/state/resolver.ts: -------------------------------------------------------------------------------- 1 | import { Id } from '@iden3/js-iden3-core'; 2 | import { ethers } from 'ethers'; 3 | import { ICache, createInMemoryCache } from '@0xpolygonid/js-sdk'; 4 | import { Abi, Abi__factory } from '@lib/state/types/ethers-contracts'; 5 | import { IState } from '@lib/state/types/ethers-contracts/Abi'; 6 | import { CONSTANTS } from '@lib/constants'; 7 | import { isRootDoesNotExistError, isStateDoesNotExistError } from '@0xpolygonid/js-sdk'; 8 | 9 | export type Resolvers = { 10 | [key: string]: IStateResolver; 11 | }; 12 | 13 | export interface IStateResolver { 14 | resolve(id: bigint, state: bigint): Promise; 15 | rootResolve(state: bigint): Promise; 16 | } 17 | 18 | export type ResolvedState = { 19 | latest: boolean; 20 | genesis: boolean; 21 | state: unknown; 22 | transitionTimestamp: number | string; 23 | }; 24 | 25 | /** 26 | * Configuration options for caching behavior 27 | */ 28 | type ResolverCacheOptions = { 29 | /** TTL in milliseconds for latest states/roots (shorter since they can change) */ 30 | notReplacedTtl?: number; 31 | /** TTL in milliseconds for historical states/roots (longer since they're they can change with less probability) */ 32 | replacedTtl?: number; 33 | /** Maximum number of entries to store in cache */ 34 | maxSize?: number; 35 | }; 36 | 37 | /** 38 | * Configuration options for the EthStateResolver 39 | */ 40 | export type ResolverOptions = { 41 | /** Configuration for state resolution caching */ 42 | stateCacheOptions?: { 43 | /** Custom cache implementation (if not provided, uses in-memory cache) */ 44 | cache?: ICache; 45 | } & ResolverCacheOptions; 46 | /** Configuration for GIST root resolution caching */ 47 | rootCacheOptions?: { 48 | /** Custom cache implementation (if not provided, uses in-memory cache) */ 49 | cache?: ICache; 50 | } & ResolverCacheOptions; 51 | /** Whether to skip fetch setup for the ethers provider */ 52 | skipFetchSetup?: boolean; 53 | }; 54 | 55 | /** 56 | * Ethereum-based state resolver that resolves identity states and GIST roots 57 | * from a smart contract deployed on an Ethereum-compatible blockchain. 58 | * 59 | * This resolver caches results with different TTL values: 60 | * - Latest states/roots: shorter TTL since they can transition to historical 61 | * - Historical states/roots: longer TTL since they are immutable once replaced 62 | */ 63 | export class EthStateResolver implements IStateResolver { 64 | private _contract: Abi; 65 | private _stateResolveCache: ICache; 66 | private _rootResolveCache: ICache; 67 | private _stateCacheOptions: Required; 68 | private _rootCacheOptions: Required; 69 | 70 | /** 71 | * Creates a new EthStateResolver instance 72 | * @param rpcUrl - The RPC URL for the Ethereum-compatible blockchain 73 | * @param contractAddress - The address of the state contract 74 | * @param options - Optional configuration for caching and provider setup 75 | */ 76 | constructor(rpcUrl: string, contractAddress: string, options?: ResolverOptions) { 77 | const url = new URL(rpcUrl); 78 | const ethersProvider = new ethers.providers.JsonRpcProvider({ 79 | skipFetchSetup: options?.skipFetchSetup ?? false, 80 | url: url.href, 81 | user: url.username || undefined, 82 | password: url.password || undefined 83 | }); 84 | this._contract = Abi__factory.connect(contractAddress, ethersProvider); 85 | 86 | // Store cache options for later use 87 | this._stateCacheOptions = { 88 | notReplacedTtl: 89 | options?.stateCacheOptions?.notReplacedTtl ?? CONSTANTS.ACCEPTED_STATE_TRANSITION_DELAY / 2, 90 | replacedTtl: 91 | options?.stateCacheOptions?.replacedTtl ?? CONSTANTS.ACCEPTED_STATE_TRANSITION_DELAY, 92 | maxSize: options?.stateCacheOptions?.maxSize ?? CONSTANTS.DEFAULT_CACHE_MAX_SIZE 93 | }; 94 | this._rootCacheOptions = { 95 | replacedTtl: 96 | options?.rootCacheOptions?.replacedTtl ?? CONSTANTS.ACCEPTED_STATE_TRANSITION_DELAY, 97 | notReplacedTtl: 98 | options?.rootCacheOptions?.notReplacedTtl ?? CONSTANTS.ACCEPTED_STATE_TRANSITION_DELAY / 2, 99 | maxSize: options?.rootCacheOptions?.maxSize ?? CONSTANTS.DEFAULT_CACHE_MAX_SIZE 100 | }; 101 | 102 | // Initialize cache instances 103 | this._stateResolveCache = 104 | options?.stateCacheOptions?.cache ?? 105 | createInMemoryCache({ 106 | maxSize: this._stateCacheOptions.maxSize, 107 | ttl: this._stateCacheOptions.replacedTtl 108 | }); 109 | 110 | this._rootResolveCache = 111 | options?.rootCacheOptions?.cache ?? 112 | createInMemoryCache({ 113 | maxSize: this._rootCacheOptions.maxSize, 114 | ttl: this._rootCacheOptions.replacedTtl 115 | }); 116 | } 117 | 118 | private getCacheKey(id: bigint, state: bigint): string { 119 | return `${id.toString()}-${state.toString()}`; 120 | } 121 | 122 | private getRootCacheKey(root: bigint): string { 123 | return root.toString(); 124 | } 125 | 126 | public async resolve(id: bigint, state: bigint): Promise { 127 | const cacheKey = this.getCacheKey(id, state); 128 | 129 | // Check cache first 130 | const cachedResult = await this._stateResolveCache?.get(cacheKey); 131 | if (cachedResult) { 132 | return cachedResult; 133 | } 134 | 135 | // Perform the actual resolution 136 | const result = await this.performResolve(id, state); 137 | 138 | // Cache the result with appropriate TTL based on whether it's latest or historical 139 | const ttl = 140 | result.transitionTimestamp === 0 141 | ? this._stateCacheOptions.notReplacedTtl 142 | : this._stateCacheOptions.replacedTtl; 143 | 144 | await this._stateResolveCache?.set(cacheKey, result, ttl); 145 | 146 | return result; 147 | } 148 | 149 | private async performResolve(id: bigint, state: bigint): Promise { 150 | // check if id is genesis 151 | const isGenesis = isGenesisStateId(id, state); 152 | 153 | let contractState: IState.StateInfoStructOutput; 154 | try { 155 | contractState = await this._contract.getStateInfoByIdAndState(id, state); 156 | } catch (e) { 157 | if (isStateDoesNotExistError(e)) { 158 | if (isGenesis) { 159 | return { 160 | latest: true, 161 | genesis: isGenesis, 162 | state, 163 | transitionTimestamp: 0 164 | }; 165 | } 166 | throw new Error('State is not genesis and not registered in the smart contract'); 167 | } 168 | throw e; 169 | } 170 | 171 | if (!contractState.id.eq(id)) { 172 | throw new Error(`state was recorded for another identity`); 173 | } 174 | 175 | if (!contractState.state.eq(state)) { 176 | if (contractState.replacedAtTimestamp.eq(0n)) { 177 | throw new Error(`no information about state transition`); 178 | } 179 | return { 180 | latest: false, 181 | genesis: false, 182 | state, 183 | transitionTimestamp: contractState.replacedAtTimestamp.toNumber() 184 | }; 185 | } 186 | 187 | return { 188 | latest: contractState.replacedAtTimestamp.isZero(), 189 | genesis: isGenesis, 190 | state, 191 | transitionTimestamp: contractState.replacedAtTimestamp.toNumber() 192 | }; 193 | } 194 | 195 | public async rootResolve(root: bigint): Promise { 196 | const cacheKey = this.getRootCacheKey(root); 197 | 198 | // Check cache first 199 | const cachedResult = await this._rootResolveCache?.get(cacheKey); 200 | 201 | if (cachedResult) { 202 | return cachedResult; 203 | } 204 | 205 | // Perform the actual root resolution 206 | const result = await this.performRootResolve(root); 207 | 208 | // Cache the result with appropriate TTL based on whether it's latest or historical 209 | const ttl = result.latest 210 | ? this._rootCacheOptions.notReplacedTtl 211 | : this._rootCacheOptions.replacedTtl; 212 | 213 | await this._rootResolveCache?.set(cacheKey, result, ttl); 214 | 215 | return result; 216 | } 217 | 218 | private async performRootResolve(root: bigint): Promise { 219 | let globalStateInfo: IState.GistRootInfoStructOutput; 220 | try { 221 | globalStateInfo = await this._contract.getGISTRootInfo(root); 222 | } catch (e: unknown) { 223 | if (isRootDoesNotExistError(e)) { 224 | throw new Error('GIST root does not exist in the smart contract'); 225 | } 226 | throw e; 227 | } 228 | 229 | if (!globalStateInfo.root.eq(root)) { 230 | throw new Error(`gist info contains invalid state`); 231 | } 232 | 233 | if (!globalStateInfo.replacedByRoot.eq(0n)) { 234 | if (globalStateInfo.replacedAtTimestamp.eq(0n)) { 235 | throw new Error(`state was replaced, but replaced time unknown`); 236 | } 237 | return { 238 | latest: false, 239 | state: root, 240 | transitionTimestamp: globalStateInfo.replacedAtTimestamp.toString(), 241 | genesis: false 242 | }; 243 | } 244 | 245 | return { 246 | latest: true, 247 | state: root, 248 | transitionTimestamp: 0, 249 | genesis: false 250 | }; 251 | } 252 | } 253 | 254 | export function isGenesisStateId(id: bigint, state: bigint): boolean { 255 | const userID = Id.fromBigInt(id); 256 | const identifier = Id.idGenesisFromIdenState(userID.type(), state); 257 | return userID.equal(identifier); 258 | } 259 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2023 0kims Association 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /test/cache.test.ts: -------------------------------------------------------------------------------- 1 | import { ICache, createInMemoryCache } from '@0xpolygonid/js-sdk'; 2 | 3 | describe('Cache', () => { 4 | describe('Basic Operations', () => { 5 | let cache: ICache; 6 | 7 | beforeEach(() => { 8 | cache = createInMemoryCache({ ttl: 1000, maxSize: 1000 }); 9 | }); 10 | 11 | it('should set and get values', async () => { 12 | await cache.set('key1', 'value1'); 13 | const result = await cache.get('key1'); 14 | expect(result).toBe('value1'); 15 | }); 16 | 17 | it('should return undefined for non-existent keys', async () => { 18 | const result = await cache.get('nonexistent'); 19 | expect(result).toBeUndefined(); 20 | }); 21 | 22 | it('should delete values', async () => { 23 | await cache.set('key1', 'value1'); 24 | await cache.delete('key1'); 25 | const result = await cache.get('key1'); 26 | expect(result).toBeUndefined(); 27 | }); 28 | 29 | it('should handle multiple keys', async () => { 30 | await cache.set('key1', 'value1'); 31 | await cache.set('key2', 'value2'); 32 | await cache.set('key3', 'value3'); 33 | 34 | expect(await cache.get('key1')).toBe('value1'); 35 | expect(await cache.get('key2')).toBe('value2'); 36 | expect(await cache.get('key3')).toBe('value3'); 37 | }); 38 | 39 | it('should overwrite existing values', async () => { 40 | await cache.set('key1', 'oldValue'); 41 | await cache.set('key1', 'newValue'); 42 | const result = await cache.get('key1'); 43 | expect(result).toBe('newValue'); 44 | }); 45 | 46 | it('should clear all entries', async () => { 47 | await cache.set('key1', 'value1'); 48 | await cache.set('key2', 'value2'); 49 | await cache.set('key3', 'value3'); 50 | 51 | await cache.clear(); 52 | 53 | expect(await cache.get('key1')).toBeUndefined(); 54 | expect(await cache.get('key2')).toBeUndefined(); 55 | expect(await cache.get('key3')).toBeUndefined(); 56 | }); 57 | 58 | it('should handle deleting non-existent keys gracefully', async () => { 59 | await expect(cache.delete('nonexistent')).resolves.toBeUndefined(); 60 | }); 61 | }); 62 | 63 | describe('TTL and Expiration', () => { 64 | it('should expire entries after TTL', async () => { 65 | const cache = createInMemoryCache({ ttl: 50, maxSize: 1000 }); 66 | 67 | await cache.set('key1', 'value1'); 68 | expect(await cache.get('key1')).toBe('value1'); 69 | 70 | // Wait for expiration 71 | await new Promise((resolve) => setTimeout(resolve, 60)); 72 | expect(await cache.get('key1')).toBeUndefined(); 73 | }); 74 | 75 | it('should handle negative TTL (immediate expiration)', async () => { 76 | const cache = createInMemoryCache({ ttl: -100, maxSize: 1000 }); 77 | await cache.set('key1', 'value1'); 78 | expect(await cache.get('key1')).toBeUndefined(); 79 | }); 80 | 81 | it('should reset expiration when updating existing key', async () => { 82 | const cache = createInMemoryCache({ ttl: 100, maxSize: 1000 }); 83 | 84 | await cache.set('key1', 'value1'); 85 | await new Promise((resolve) => setTimeout(resolve, 50)); 86 | 87 | // Update the value (should reset expiration) 88 | await cache.set('key1', 'updatedValue'); 89 | await new Promise((resolve) => setTimeout(resolve, 60)); 90 | 91 | // Should still be available 92 | expect(await cache.get('key1')).toBe('updatedValue'); 93 | 94 | // Wait for final expiration 95 | await new Promise((resolve) => setTimeout(resolve, 50)); 96 | expect(await cache.get('key1')).toBeUndefined(); 97 | }); 98 | 99 | it('should store values permanently when no TTL is set', async () => { 100 | const cache = createInMemoryCache({ maxSize: 1000 }); 101 | 102 | await cache.set('permanent', 'value'); 103 | await new Promise((resolve) => setTimeout(resolve, 200)); 104 | 105 | expect(await cache.get('permanent')).toBe('value'); 106 | }); 107 | 108 | it('should handle explicit undefined TTL', async () => { 109 | const cache = createInMemoryCache({ ttl: undefined, maxSize: 1000 }); 110 | 111 | await cache.set('key1', 'value1'); 112 | await new Promise((resolve) => setTimeout(resolve, 100)); 113 | 114 | expect(await cache.get('key1')).toBe('value1'); 115 | }); 116 | }); 117 | 118 | describe('Memory Management and Size Limits', () => { 119 | it('should enforce maxSize limit by evicting the least recently used item', async () => { 120 | const cache = createInMemoryCache({ ttl: 10000, maxSize: 3 }); 121 | 122 | await cache.set('key1', 'value1'); 123 | await cache.set('key2', 'value2'); 124 | await cache.set('key3', 'value3'); 125 | 126 | // key1 is the least recently used 127 | await cache.set('key4', 'value4'); 128 | 129 | expect(await cache.get('key2')).toBe('value2'); 130 | expect(await cache.get('key4')).toBeDefined(); 131 | expect(await cache.get('key3')).toBe('value3'); 132 | expect(await cache.get('key1')).toBeUndefined(); // evicted 133 | }); 134 | 135 | it('should cleanup expired entries before enforcing maxSize', async () => { 136 | const cache = createInMemoryCache({ ttl: 50, maxSize: 3 }); 137 | 138 | // Fill to capacity 139 | await cache.set('key1', 'value1'); 140 | await cache.set('key2', 'value2'); 141 | await cache.set('key3', 'value3'); 142 | 143 | // Wait for expiration 144 | await new Promise((resolve) => setTimeout(resolve, 60)); 145 | 146 | // Should be able to add new entry since expired ones can be cleaned up 147 | await expect(cache.set('newKey', 'newValue')).resolves.toBeUndefined(); 148 | expect(await cache.get('newKey')).toBe('newValue'); 149 | }); 150 | 151 | it('should handle cache operations after expired entries cleanup', async () => { 152 | const cache = createInMemoryCache({ ttl: 50, maxSize: 1000 }); 153 | 154 | await cache.set('key1', 'value1'); 155 | await cache.set('key2', 'value2'); 156 | 157 | // Wait for expiration 158 | await new Promise((resolve) => setTimeout(resolve, 60)); 159 | 160 | expect(await cache.get('key2')).toBeUndefined(); 161 | expect(await cache.get('key1')).toBeUndefined(); 162 | 163 | // Should be able to add new entries after cleanup 164 | await cache.set('key3', 'value3'); 165 | expect(await cache.get('key3')).toBe('value3'); 166 | }); 167 | }); 168 | 169 | describe('Data Types Support', () => { 170 | interface TestObject { 171 | id: number; 172 | name: string; 173 | nested: { prop: boolean }; 174 | } 175 | 176 | it('should handle object types', async () => { 177 | const cache = createInMemoryCache({ ttl: 1000, maxSize: 1000 }); 178 | 179 | const testObj: TestObject = { 180 | id: 1, 181 | name: 'test', 182 | nested: { prop: true } 183 | }; 184 | 185 | await cache.set('object', testObj); 186 | const result = await cache.get('object'); 187 | 188 | expect(result).toEqual(testObj); 189 | expect(result?.nested.prop).toBe(true); 190 | }); 191 | 192 | it('should handle array types', async () => { 193 | const cache = createInMemoryCache({ ttl: 1000, maxSize: 1000 }); 194 | 195 | const testArray = [1, 2, 3, 4, 5]; 196 | await cache.set('array', testArray); 197 | const result = await cache.get('array'); 198 | 199 | expect(result).toEqual(testArray); 200 | expect(result?.length).toBe(5); 201 | }); 202 | 203 | it('should handle null values', async () => { 204 | const cache = createInMemoryCache({ ttl: 1000, maxSize: 1000 }); 205 | 206 | await cache.set('nullValue', null); 207 | const result = await cache.get('nullValue'); 208 | 209 | expect(result).toBeNull(); 210 | }); 211 | }); 212 | 213 | describe('Concurrency', () => { 214 | it('should handle concurrent operations safely', async () => { 215 | const cache = createInMemoryCache({ ttl: 1000, maxSize: 1000 }); 216 | 217 | // Set multiple values concurrently 218 | const setPromises = Array.from({ length: 20 }, (_, i) => cache.set(`key${i}`, `value${i}`)); 219 | await Promise.all(setPromises); 220 | 221 | // Get all values concurrently 222 | const getPromises = Array.from({ length: 20 }, (_, i) => cache.get(`key${i}`)); 223 | const results = await Promise.all(getPromises); 224 | 225 | // Verify all results 226 | results.forEach((result, i) => { 227 | expect(result).toBe(`value${i}`); 228 | }); 229 | }); 230 | 231 | it('should handle mixed concurrent operations', async () => { 232 | const cache = createInMemoryCache({ ttl: 5000, maxSize: 1000 }); 233 | 234 | // Mix of set, get, and delete operations 235 | const operations = [ 236 | ...Array.from({ length: 10 }, (_, i) => cache.set(`key${i}`, `value${i}`)), 237 | ...Array.from({ length: 5 }, (_, i) => cache.get(`key${i}`)), 238 | ...Array.from({ length: 3 }, (_, i) => cache.delete(`key${i + 10}`)) 239 | ]; 240 | 241 | const results = await Promise.all(operations); 242 | 243 | // Verify set operations completed (they return undefined) 244 | expect(results.slice(0, 10).every((result) => result === undefined)).toBe(true); 245 | 246 | // Verify get operations 247 | const getResults = results.slice(10, 15); 248 | expect(getResults.every((result, i) => result === `value${i}`)).toBe(true); 249 | }); 250 | }); 251 | 252 | describe('Performance Tests', () => { 253 | it('should handle high-volume operations efficiently', async () => { 254 | const cache = createInMemoryCache({ ttl: 10000, maxSize: 10_000 }); 255 | const itemCount = 5_000; 256 | 257 | // Measure set operations 258 | const setStart = process.hrtime(); 259 | for (let i = 0; i < itemCount; i++) { 260 | await cache.set(`key${i}`, `value${i}`); 261 | } 262 | const setEnd = process.hrtime(setStart); 263 | const setTimeMs = setEnd[0] * 1000 + setEnd[1] / 1000000; 264 | 265 | // Measure get operations 266 | const getStart = process.hrtime(); 267 | for (let i = 0; i < itemCount; i++) { 268 | await cache.get(`key${i}`); 269 | } 270 | const getEnd = process.hrtime(getStart); 271 | const getTimeMs = getEnd[0] * 1000 + getEnd[1] / 1000000; 272 | 273 | // Performance expectations (these are generous to avoid flaky tests) 274 | expect(setTimeMs).toBeLessThan(5000); // 5 seconds for 5000 sets 275 | expect(getTimeMs).toBeLessThan(2000); // 2 seconds for 5000 gets 276 | 277 | // Verify some items are still accessible (cache should retain at least some items) 278 | const sampleResults = await Promise.all([ 279 | cache.get('key0'), 280 | cache.get('key1000'), 281 | cache.get('key2000') 282 | ]); 283 | expect(sampleResults.some((result) => result !== undefined)).toBe(true); 284 | 285 | console.log( 286 | `Performance: ${itemCount} sets in ${setTimeMs.toFixed( 287 | 2 288 | )}ms, ${itemCount} gets in ${getTimeMs.toFixed(2)}ms` 289 | ); 290 | }); 291 | 292 | it('should efficiently handle batch operations', async () => { 293 | const cache = createInMemoryCache({ ttl: 10000, maxSize: 5000 }); 294 | const batchSize = 1000; 295 | 296 | const batchStart = process.hrtime(); 297 | 298 | // Batch set operations 299 | const setPromises = Array.from({ length: batchSize }, (_, i) => 300 | cache.set(`batch_key${i}`, `batch_value${i}`) 301 | ); 302 | await Promise.all(setPromises); 303 | 304 | // Batch get operations 305 | const getPromises = Array.from({ length: batchSize }, (_, i) => cache.get(`batch_key${i}`)); 306 | const results = await Promise.all(getPromises); 307 | 308 | const batchEnd = process.hrtime(batchStart); 309 | const batchTimeMs = batchEnd[0] * 1000 + batchEnd[1] / 1000000; 310 | 311 | // Verify all operations completed successfully 312 | expect(results.every((result, i) => result === `batch_value${i}`)).toBe(true); 313 | 314 | // Performance expectation 315 | expect(batchTimeMs).toBeLessThan(3000); // 3 seconds for 1000 concurrent ops 316 | 317 | console.log(`Batch Performance: ${batchSize} concurrent ops in ${batchTimeMs.toFixed(2)}ms`); 318 | }); 319 | 320 | it('should efficiently cleanup expired entries at scale', async () => { 321 | const cache = createInMemoryCache({ ttl: 50, maxSize: 5000 }); 322 | const itemCount = 2000; 323 | 324 | // Add many entries that will expire 325 | for (let i = 0; i < itemCount; i++) { 326 | await cache.set(`expire_key${i}`, `expire_value${i}`); 327 | } 328 | 329 | // Wait for expiration 330 | await new Promise((resolve) => setTimeout(resolve, 60)); 331 | 332 | // QuickLRU handles expiration lazily - trigger cleanup by accessing items 333 | const cleanupStart = process.hrtime(); 334 | 335 | // Access all expired items to trigger lazy cleanup 336 | // QuickLRU will remove expired items when they are accessed 337 | let cleanedUpCount = 0; 338 | for (let i = 0; i < itemCount; i++) { 339 | const result = await cache.get(`expire_key${i}`); 340 | if (result === undefined) { 341 | cleanedUpCount++; 342 | } 343 | } 344 | 345 | const cleanupEnd = process.hrtime(cleanupStart); 346 | const cleanupTimeMs = cleanupEnd[0] * 1000 + cleanupEnd[1] / 1000000; 347 | 348 | // All items should be expired and cleaned up during access 349 | expect(cleanedUpCount).toBe(itemCount); 350 | expect(cleanupTimeMs).toBeLessThan(500); // 500ms for cleanup of 2000 items 351 | 352 | console.log( 353 | `Cleanup Performance: ${itemCount} expired items cleaned in ${cleanupTimeMs.toFixed(2)}ms` 354 | ); 355 | }); 356 | 357 | it('should maintain performance with mixed expired and valid entries', async () => { 358 | const cache = createInMemoryCache({ ttl: 10000, maxSize: 3000 }); 359 | 360 | // Add items that won't expire 361 | const validItems = 500; 362 | for (let i = 0; i < validItems; i++) { 363 | await cache.set(`valid_key${i}`, `valid_value${i}`); 364 | } 365 | 366 | // Add items with short expiry 367 | const shortCache = createInMemoryCache({ ttl: 30, maxSize: 3000 }); 368 | const expiredItems = 1000; 369 | for (let i = 0; i < expiredItems; i++) { 370 | await shortCache.set(`expired_key${i}`, `expired_value${i}`); 371 | } 372 | 373 | // Wait for some to expire 374 | await new Promise((resolve) => setTimeout(resolve, 50)); 375 | 376 | // Test mixed access patterns 377 | const mixedStart = process.hrtime(); 378 | 379 | // Access valid items 380 | for (let i = 0; i < 100; i++) { 381 | await cache.get(`valid_key${i}`); 382 | } 383 | 384 | // Access expired items (should be fast since they're cleaned up on access) 385 | for (let i = 0; i < 100; i++) { 386 | await shortCache.get(`expired_key${i}`); 387 | } 388 | 389 | const mixedEnd = process.hrtime(mixedStart); 390 | const mixedTimeMs = mixedEnd[0] * 1000 + mixedEnd[1] / 1000000; 391 | 392 | expect(mixedTimeMs).toBeLessThan(500); // 500ms for 200 mixed operations 393 | 394 | console.log(`Mixed Access Performance: 200 mixed operations in ${mixedTimeMs.toFixed(2)}ms`); 395 | }); 396 | }); 397 | 398 | describe('Memory Efficiency', () => { 399 | it('should not grow unbounded with expired entries', async () => { 400 | const cache = createInMemoryCache({ ttl: 30, maxSize: 10_000 }); 401 | 402 | // Simulate continuous usage with expiring entries 403 | for (let batch = 0; batch < 5; batch++) { 404 | // Add batch of entries 405 | for (let i = 0; i < 200; i++) { 406 | await cache.set(`batch${batch}_key${i}`, `batch${batch}_value${i}`); 407 | } 408 | 409 | // Wait for expiration 410 | await new Promise((resolve) => setTimeout(resolve, 40)); 411 | 412 | // Trigger lazy cleanup by trying to access some items 413 | await cache.get(`batch${batch}_key0`); 414 | } 415 | 416 | // Verify cache still works with new entries 417 | await cache.set('final_test', 'final_value'); 418 | expect(await cache.get('final_test')).toBe('final_value'); 419 | 420 | console.log( 421 | `Memory Efficiency: Cache remains functional after multiple batches with expiration` 422 | ); 423 | }); 424 | }); 425 | }); 426 | -------------------------------------------------------------------------------- /src/auth/auth.ts: -------------------------------------------------------------------------------- 1 | import { AuthPubSignals } from '@lib/circuits/auth'; 2 | import { Query } from '@lib/circuits/query'; 3 | import { v4 as uuidv4 } from 'uuid'; 4 | 5 | import { Resolvers } from '@lib/state/resolver'; 6 | import { Circuits, VerifyOpts } from '@lib/circuits/registry'; 7 | import { proving, Token } from '@iden3/js-jwz'; 8 | import { 9 | AuthorizationRequestMessage, 10 | AuthorizationResponseMessage, 11 | CircuitId, 12 | IPacker, 13 | JWSPacker, 14 | KMS, 15 | PackageManager, 16 | ProvingParams, 17 | PROTOCOL_CONSTANTS, 18 | VerificationHandlerFunc, 19 | VerificationParams, 20 | ZKPPacker, 21 | NativeProver, 22 | IZKProver, 23 | FSCircuitStorage, 24 | ICircuitStorage, 25 | cacheLoader, 26 | byteEncoder, 27 | JSONObject, 28 | verifyExpiresTime, 29 | parseAcceptProfile 30 | } from '@0xpolygonid/js-sdk'; 31 | import { Resolvable } from 'did-resolver'; 32 | import { Options, DocumentLoader } from '@iden3/js-jsonld-merklization'; 33 | import path from 'path'; 34 | import { DID, getUnixTimestamp } from '@iden3/js-iden3-core'; 35 | import { ZeroKnowledgeProofRequest } from '@0xpolygonid/js-sdk'; 36 | 37 | /** 38 | * Options to pass to createAuthorizationRequest function 39 | * @public 40 | */ 41 | export type AuthorizationRequestCreateOptions = { 42 | accept?: string[]; 43 | scope?: ZeroKnowledgeProofRequest[]; 44 | expires_time?: Date; 45 | }; 46 | 47 | /** 48 | * createAuthorizationRequest is a function to create protocol authorization request 49 | * @param {string} reason - reason to request proof 50 | * @param {string} sender - sender did 51 | * @param {string} callbackUrl - callback that user should use to send response 52 | * @returns `Promise` 53 | */ 54 | export function createAuthorizationRequest( 55 | reason: string, 56 | sender: string, 57 | callbackUrl: string, 58 | opts?: AuthorizationRequestCreateOptions 59 | ): AuthorizationRequestMessage { 60 | return createAuthorizationRequestWithMessage(reason, '', sender, callbackUrl, opts); 61 | } 62 | /** 63 | * createAuthorizationRequestWithMessage is a function to create protocol authorization request with explicit message to sign 64 | * @param {string} reason - reason to request proof 65 | * @param {string} message - message to sign in the response 66 | * @param {string} sender - sender did 67 | * @param {string} callbackUrl - callback that user should use to send response 68 | * @returns `Promise` 69 | */ 70 | export function createAuthorizationRequestWithMessage( 71 | reason: string, 72 | message: string, 73 | sender: string, 74 | callbackUrl: string, 75 | opts?: AuthorizationRequestCreateOptions 76 | ): AuthorizationRequestMessage { 77 | const uuid = uuidv4(); 78 | const request: AuthorizationRequestMessage = { 79 | id: uuid, 80 | thid: uuid, 81 | from: sender, 82 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 83 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, 84 | body: { 85 | accept: opts?.accept, 86 | reason: reason, 87 | message: message, 88 | callbackUrl: callbackUrl, 89 | scope: opts?.scope || [] 90 | }, 91 | created_time: getUnixTimestamp(new Date()), 92 | expires_time: opts?.expires_time ? getUnixTimestamp(opts.expires_time) : undefined 93 | }; 94 | return request; 95 | } 96 | /** 97 | * VerifierParams are params to pass init verifier that contain jsonld document loader options and 98 | * options to verify the query 99 | */ 100 | export type VerifierParams = Options & { 101 | /* resolvers for state of the identities */ 102 | stateResolver: Resolvers; 103 | /* didDocumentResolver to init default jws packer */ 104 | didDocumentResolver?: Resolvable; 105 | /* circuitsDir - directory where circuits files are stored (default - 'circuits') */ 106 | circuitsDir?: string; 107 | /* suite - optional suite with prover, circuit storage, package manager and document loader */ 108 | suite?: VerifierSuiteParams; 109 | }; 110 | 111 | /** 112 | * VerifierSuiteParams are custom defined prover, circuit storage, package manager and document loader 113 | */ 114 | export interface VerifierSuiteParams { 115 | documentLoader: DocumentLoader; 116 | packageManager: PackageManager; 117 | circuitStorage: ICircuitStorage; 118 | prover: IZKProver; 119 | } 120 | /** 121 | * 122 | * Verifier is responsible for verification of JWZ / JWS packed messages with zero-knowledge proofs inside. 123 | * 124 | * @public 125 | * @class Verifier 126 | */ 127 | export class Verifier { 128 | private schemaLoader: DocumentLoader; 129 | private stateResolver: Resolvers; 130 | 131 | private packageManager: PackageManager; 132 | private prover: IZKProver; 133 | private circuitStorage: ICircuitStorage; 134 | 135 | /** 136 | * Creates an instance of the Verifier. 137 | * @private 138 | * @param {Resolvers} resolvers - state resolvers instances 139 | * @param {VerifierSuiteParams} params - suite for verification 140 | */ 141 | private constructor(stateResolver: Resolvers, params: VerifierSuiteParams) { 142 | this.schemaLoader = params.documentLoader; 143 | this.stateResolver = stateResolver; 144 | this.packageManager = params.packageManager; 145 | this.circuitStorage = params.circuitStorage; 146 | this.prover = params.prover; 147 | } 148 | 149 | /** 150 | * Creates an instance of the Verifier. 151 | * @public 152 | * @param {VerifierParams} params - params to init verifier 153 | * @returns `Promise` 154 | */ 155 | static async newVerifier(params: VerifierParams): Promise { 156 | if (!params.suite) { 157 | const documentLoader = (params as Options).documentLoader ?? cacheLoader(params as Options); 158 | const dirname = params?.circuitsDir ?? path.join(process.cwd(), 'circuits'); 159 | const circuitStorage = new FSCircuitStorage({ dirname }); 160 | params.suite = { 161 | documentLoader, 162 | circuitStorage, 163 | prover: new NativeProver(circuitStorage), 164 | packageManager: new PackageManager() 165 | }; 166 | const verifier = new Verifier(params.stateResolver, params.suite); 167 | await verifier.initPackers(params.didDocumentResolver); 168 | return verifier; 169 | } 170 | return new Verifier(params.stateResolver, params.suite); 171 | } 172 | 173 | // setPackageManager sets the package manager for the Verifier. 174 | public setPackageManager(manager: PackageManager) { 175 | this.packageManager = manager; 176 | } 177 | 178 | // setPacker sets the custom packer manager for the Verifier. 179 | public setPacker(packer: IPacker) { 180 | return this.packageManager.registerPackers([packer]); 181 | } 182 | 183 | /** 184 | @deprecated, use setupAuthZKPPacker for AuthV2/AuthV3/ AuthV3_8_32 circuits support 185 | setupAuthV2ZKPPacker sets the custom packer manager for the Verifier. 186 | **/ 187 | public async setupAuthV2ZKPPacker(circuitStorage: ICircuitStorage) { 188 | if (!circuitStorage) { 189 | throw new Error('circuit storage is not defined'); 190 | } 191 | const authV2Set = await circuitStorage.loadCircuitData(CircuitId.AuthV2); 192 | 193 | if (!authV2Set.verificationKey) { 194 | throw new Error('verification key is not for authv2 circuit'); 195 | } 196 | const mapKey = proving.provingMethodGroth16AuthV2Instance.methodAlg.toString(); 197 | const provingParamMap: Map = new Map(); 198 | 199 | const stateVerificationFn = async ( 200 | circuitId: string, 201 | pubSignals: Array 202 | ): Promise => { 203 | if (circuitId !== CircuitId.AuthV2) { 204 | throw new Error(`CircuitId is not supported ${circuitId}`); 205 | } 206 | 207 | const verifier = new AuthPubSignals(pubSignals); 208 | await verifier.verifyStates(this.stateResolver); 209 | return true; 210 | }; 211 | 212 | const verificationFn = new VerificationHandlerFunc(stateVerificationFn); 213 | 214 | const verificationParamMap: Map = new Map(); 215 | verificationParamMap.set(mapKey, { 216 | key: authV2Set.verificationKey, 217 | verificationFn 218 | }); 219 | 220 | const zkpPacker = new ZKPPacker(provingParamMap, verificationParamMap); 221 | return this.setPacker(zkpPacker); 222 | } 223 | 224 | // setupAuthZKPPacker sets the custom packer manager for the Verifier. 225 | public async setupAuthZKPPacker(circuitStorage: ICircuitStorage) { 226 | if (!circuitStorage) { 227 | throw new Error('circuit storage is not defined'); 228 | } 229 | const authV2Set = await circuitStorage.loadCircuitData(CircuitId.AuthV2); 230 | const authV3Set = await circuitStorage.loadCircuitData(CircuitId.AuthV3); 231 | const authV3_8_32Set = await circuitStorage.loadCircuitData(CircuitId.AuthV3_8_32); 232 | 233 | if (!authV2Set.verificationKey) { 234 | throw new Error('verification key is not for authV2 circuit'); 235 | } 236 | 237 | const mapKeyAuthV2 = proving.provingMethodGroth16AuthV2Instance.methodAlg.toString(); 238 | const mapKeyAuthV3 = proving.provingMethodGroth16AuthV3Instance.methodAlg.toString(); 239 | const mapKeyAuthV3_8_32 = proving.provingMethodGroth16AuthV3_8_32Instance.methodAlg.toString(); 240 | const provingParamMap: Map = new Map(); 241 | 242 | const stateVerificationFn = async ( 243 | circuitId: string, 244 | pubSignals: Array 245 | ): Promise => { 246 | if ( 247 | circuitId !== CircuitId.AuthV2 && 248 | circuitId !== CircuitId.AuthV3 && 249 | circuitId !== CircuitId.AuthV3_8_32 250 | ) { 251 | throw new Error(`CircuitId is not supported ${circuitId}`); 252 | } 253 | 254 | const verifier = new AuthPubSignals(pubSignals); 255 | await verifier.verifyStates(this.stateResolver); 256 | return true; 257 | }; 258 | 259 | const verificationFn = new VerificationHandlerFunc(stateVerificationFn); 260 | 261 | const verificationParamMap: Map = new Map(); 262 | verificationParamMap.set(mapKeyAuthV2, { 263 | key: authV2Set.verificationKey, 264 | verificationFn 265 | }); 266 | 267 | authV3Set.verificationKey && 268 | verificationParamMap.set(mapKeyAuthV3, { 269 | key: authV3Set.verificationKey, 270 | verificationFn 271 | }); 272 | authV3_8_32Set.verificationKey && 273 | verificationParamMap.set(mapKeyAuthV3_8_32, { 274 | key: authV3_8_32Set.verificationKey, 275 | verificationFn 276 | }); 277 | 278 | const zkpPacker = new ZKPPacker(provingParamMap, verificationParamMap); 279 | return this.setPacker(zkpPacker); 280 | } 281 | 282 | // setupJWSPacker sets the JWS packer for the Verifier. 283 | public setupJWSPacker(kms: KMS, documentResolver: Resolvable) { 284 | const jwsPacker = new JWSPacker(kms, documentResolver); 285 | return this.setPacker(jwsPacker); 286 | } 287 | 288 | public verifyAuthRequest(request: AuthorizationRequestMessage, opts?: VerifyOpts) { 289 | if (!opts?.allowExpiredMessages) { 290 | verifyExpiresTime(request); 291 | } 292 | this.verifyProfile(request.type, request.body.accept); 293 | const groupIdValidationMap: { [k: string]: ZeroKnowledgeProofRequest[] } = {}; 294 | const requestScope = request.body.scope; 295 | for (const proofRequest of requestScope) { 296 | const groupId = proofRequest.query.groupId as number; 297 | if (groupId) { 298 | const existingRequests = groupIdValidationMap[groupId] ?? []; 299 | 300 | //validate that all requests in the group have the same schema, issuer and circuit 301 | for (const existingRequest of existingRequests) { 302 | if (existingRequest.query.type !== proofRequest.query.type) { 303 | throw new Error(`all requests in the group should have the same type`); 304 | } 305 | 306 | if (existingRequest.query.context !== proofRequest.query.context) { 307 | throw new Error(`all requests in the group should have the same context`); 308 | } 309 | 310 | const allowedIssuers = proofRequest.query.allowedIssuers as string[]; 311 | const existingRequestAllowedIssuers = existingRequest.query.allowedIssuers as string[]; 312 | if ( 313 | !( 314 | allowedIssuers.includes('*') || 315 | allowedIssuers.every((issuer) => existingRequestAllowedIssuers.includes(issuer)) 316 | ) 317 | ) { 318 | throw new Error(`all requests in the group should have the same issuer`); 319 | } 320 | } 321 | groupIdValidationMap[groupId] = [...(groupIdValidationMap[groupId] ?? []), proofRequest]; 322 | } 323 | } 324 | } 325 | 326 | /** 327 | * verifies zero knowledge proof response according to the proof request 328 | * @public 329 | * @param {AuthorizationResponseMessage} response - auth protocol response 330 | * @param {AuthorizationRequestMessage} proofRequest - auth protocol request 331 | * @param {VerifyOpts} opts - verification options 332 | * 333 | * @returns `Promise` 334 | */ 335 | public async verifyAuthResponse( 336 | response: AuthorizationResponseMessage, 337 | request: AuthorizationRequestMessage, 338 | opts?: VerifyOpts 339 | ) { 340 | if (!opts?.allowExpiredMessages) { 341 | verifyExpiresTime(request); 342 | } 343 | if ((request.body.message ?? '') !== (response.body.message ?? '')) { 344 | throw new Error('message for signing from request is not presented in response'); 345 | } 346 | 347 | if (request.from !== response.to) { 348 | throw new Error( 349 | `sender of the request is not a target of response - expected ${request.from}, given ${response.to}` 350 | ); 351 | } 352 | 353 | this.verifyAuthRequest(request); 354 | const requestScope = request.body.scope; 355 | 356 | const groupIdToLinkIdMap = new Map(); 357 | // group requests by query group id 358 | for (const proofRequest of requestScope) { 359 | const groupId = proofRequest.query.groupId as number; 360 | 361 | const proofResp = response.body.scope.find((resp) => resp.id === proofRequest.id); 362 | 363 | if (!proofResp) { 364 | if (proofRequest.optional) { 365 | continue; 366 | } 367 | throw new Error(`proof is not given for requestId ${proofRequest.id}`); 368 | } 369 | 370 | const circuitId = proofResp.circuitId; 371 | if (circuitId !== proofRequest.circuitId) { 372 | throw new Error( 373 | `proof is not given for requested circuit expected: ${proofRequest.circuitId}, given ${circuitId}` 374 | ); 375 | } 376 | const isValid = await this.prover.verify(proofResp, circuitId); 377 | if (!isValid) { 378 | throw new Error( 379 | `Proof with circuit id ${circuitId} and request id ${proofResp.id} is not valid` 380 | ); 381 | } 382 | 383 | const CircuitVerifier = Circuits.getCircuitPubSignals(circuitId); 384 | if (!CircuitVerifier) { 385 | throw new Error(`circuit ${circuitId} is not supported by the library`); 386 | } 387 | 388 | const params: JSONObject = proofRequest.params ?? {}; 389 | params.verifierDid = DID.parse(request.from); 390 | 391 | // verify query 392 | const verifier = new CircuitVerifier(proofResp.pub_signals); 393 | 394 | const pubSignals = await verifier.verifyQuery( 395 | proofRequest.query as unknown as Query, 396 | this.schemaLoader, 397 | proofResp.vp, 398 | opts, 399 | params 400 | ); 401 | 402 | // write linkId to the proof response 403 | const pubSig = pubSignals as unknown as { linkID?: number }; 404 | 405 | if (pubSig.linkID && groupId) { 406 | groupIdToLinkIdMap.set(groupId, [ 407 | ...(groupIdToLinkIdMap.get(groupId) ?? []), 408 | { linkID: pubSig.linkID, requestId: proofResp.id.toString() } 409 | ]); 410 | } 411 | // verify states 412 | 413 | await verifier.verifyStates(this.stateResolver, opts); 414 | 415 | if (!response.from) { 416 | throw new Error(`proof response doesn't contain from field`); 417 | } 418 | 419 | // verify id ownership 420 | await verifier.verifyIdOwnership(response.from, BigInt(proofResp.id)); 421 | } 422 | 423 | // verify grouping links 424 | 425 | for (const [groupId, metas] of groupIdToLinkIdMap.entries()) { 426 | // check that all linkIds are the same 427 | if (metas.some((meta) => meta.linkID !== metas[0].linkID)) { 428 | throw new Error( 429 | `Link id validation failed for group ${groupId}, request linkID to requestIds info: ${JSON.stringify( 430 | metas 431 | )}` 432 | ); 433 | } 434 | } 435 | } 436 | 437 | /** 438 | * verifies jwz token 439 | * @public 440 | * @param {string} tokenStr - token string 441 | * @param {VerifyOpts} opts - verification options 442 | * 443 | * @returns `Promise` 444 | */ 445 | public async verifyJWZ(tokenStr: string, opts?: VerifyOpts): Promise { 446 | const token = await Token.parse(tokenStr); 447 | const key = (await this.circuitStorage.loadCircuitData(token.circuitId as CircuitId)) 448 | .verificationKey; 449 | if (!key) { 450 | throw new Error(`verification key is not found for circuit ${token.circuitId}`); 451 | } 452 | 453 | const isValid = await token.verify(key); 454 | if (!isValid) { 455 | throw new Error(`zero-knowledge proof of jwz token is not valid`); 456 | } 457 | 458 | const CircuitVerifier = Circuits.getCircuitPubSignals(token.circuitId); 459 | 460 | if (!CircuitVerifier) { 461 | throw new Error(`circuit ${token.circuitId} is not supported by the library`); 462 | } 463 | 464 | // outputs unmarshaller 465 | const verifier = new CircuitVerifier(token.zkProof.pub_signals); 466 | 467 | // state verification 468 | await verifier.verifyStates(this.stateResolver, opts); 469 | 470 | return token; 471 | } 472 | 473 | /** 474 | * perform both verification of jwz / jws token and authorization request message 475 | * @public 476 | * @param {string} tokenStr - token string 477 | * @param {AuthorizationRequestMessage} request - auth protocol request 478 | * @param {VerifyOpts} opts - verification options 479 | * 480 | * @returns `Promise` 481 | */ 482 | public async fullVerify( 483 | tokenStr: string, 484 | request: AuthorizationRequestMessage, 485 | opts?: VerifyOpts 486 | ): Promise { 487 | const msg = await this.packageManager.unpack(byteEncoder.encode(tokenStr)); 488 | 489 | if (request.body.accept?.length) { 490 | const acceptedMediaTypes = request.body.accept.map( 491 | (accept) => parseAcceptProfile(accept).env 492 | ); 493 | if (!acceptedMediaTypes.includes(msg.unpackedMediaType)) { 494 | throw new Error('response type is not in accept profiles of the request'); 495 | } 496 | } 497 | 498 | const response = msg.unpackedMessage as AuthorizationResponseMessage; 499 | await this.verifyAuthResponse(response, request, opts); 500 | return response; 501 | } 502 | 503 | private async initPackers(didResolver?: Resolvable) { 504 | await this.setupAuthZKPPacker(this.circuitStorage); 505 | // set default jws packer if packageManager is not present in options but did document resolver is. 506 | if (didResolver) { 507 | this.setupJWSPacker(new KMS(), didResolver); 508 | } 509 | } 510 | 511 | private verifyProfile(messageType: string, profile?: string[] | undefined) { 512 | if (!profile?.length) { 513 | return; 514 | } 515 | const supportedMediaTypes: PROTOCOL_CONSTANTS.MediaType[] = []; 516 | for (const acceptProfile of profile) { 517 | const { env } = parseAcceptProfile(acceptProfile); 518 | if (this.packageManager.isProfileSupported(env, acceptProfile)) { 519 | supportedMediaTypes.push(env); 520 | } 521 | } 522 | 523 | if (!supportedMediaTypes.length) { 524 | throw new Error('no packer with profile which meets `accept` header requirements'); 525 | } 526 | } 527 | } 528 | -------------------------------------------------------------------------------- /test/linked-proofs.test.ts: -------------------------------------------------------------------------------- 1 | import { AuthorizationRequestMessage, CircuitId, ProofType } from '@0xpolygonid/js-sdk'; 2 | import { Verifier } from '@lib/auth/auth'; 3 | import { resolvers, schemaLoader, testOpts, getTestDataPath } from './mocks'; 4 | import { PROTOCOL_CONSTANTS } from '@0xpolygonid/js-sdk'; 5 | 6 | describe('Linked proofs verification', () => { 7 | it('should verification pass', async () => { 8 | const authRequest: AuthorizationRequestMessage = { 9 | id: 'f5bcdfc9-3819-4052-ad97-c059119e563c', 10 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 11 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, 12 | thid: 'f5bcdfc9-3819-4052-ad97-c059119e563c', 13 | body: { 14 | callbackUrl: 'http://localhost:8080/callback?id=1234442-123123-123123', 15 | reason: 'reason', 16 | message: 'message', 17 | scope: [ 18 | { 19 | id: 1, 20 | circuitId: CircuitId.AtomicQueryV3, 21 | optional: false, 22 | query: { 23 | proofType: ProofType.BJJSignature, 24 | allowedIssuers: ['*'], 25 | type: 'KYCAgeCredential', 26 | context: 27 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-nonmerklized.jsonld', 28 | credentialSubject: { 29 | documentType: { 30 | $eq: 99 31 | } 32 | } 33 | } 34 | }, 35 | { 36 | id: 2, 37 | circuitId: CircuitId.LinkedMultiQuery10, 38 | optional: false, 39 | query: { 40 | groupId: 1, 41 | proofType: ProofType.Iden3SparseMerkleTreeProof, 42 | allowedIssuers: ['*'], 43 | type: 'KYCEmployee', 44 | context: 45 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v101.json-ld', 46 | credentialSubject: { 47 | documentType: { 48 | $eq: 1 49 | }, 50 | position: { 51 | $eq: 'boss', 52 | $ne: 'employee' 53 | } 54 | } 55 | } 56 | }, 57 | { 58 | id: 3, 59 | circuitId: CircuitId.AtomicQueryV3, 60 | optional: false, 61 | query: { 62 | groupId: 1, 63 | proofType: ProofType.BJJSignature, 64 | allowedIssuers: ['*'], 65 | type: 'KYCEmployee', 66 | context: 67 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v101.json-ld', 68 | credentialSubject: { 69 | hireDate: { 70 | $eq: '2023-12-11' 71 | } 72 | } 73 | }, 74 | params: { 75 | nullifierSessionId: '12345' 76 | } 77 | } 78 | ] 79 | }, 80 | from: 'did:iden3:polygon:amoy:xCRp75DgAdS63W65fmXHz6p9DwdonuRU9e46DifhX' 81 | }; 82 | 83 | const tokenString = 84 | 'eyJhbGciOiJncm90aDE2IiwiY2lyY3VpdElkIjoiYXV0aFYyIiwiY3JpdCI6WyJjaXJjdWl0SWQiXSwidHlwIjoiYXBwbGljYXRpb24vaWRlbjMtemtwLWpzb24ifQ.eyJpZCI6ImViMTcyMzA2LWIzOTctNDhiZi1iMmMxLWViOGEyNGJhZDUwYyIsInR5cCI6ImFwcGxpY2F0aW9uL2lkZW4zLXprcC1qc29uIiwidHlwZSI6Imh0dHBzOi8vaWRlbjMtY29tbXVuaWNhdGlvbi5pby9hdXRob3JpemF0aW9uLzEuMC9yZXNwb25zZSIsInRoaWQiOiI1N2I0MDIyMy1kNTYxLTQ4YmQtODNjOS01MTk4NGZjZDZhMWUiLCJib2R5Ijp7Im1lc3NhZ2UiOiJtZXNzYWdlIiwic2NvcGUiOlt7ImlkIjoxLCJjaXJjdWl0SWQiOiJjcmVkZW50aWFsQXRvbWljUXVlcnlWMy1iZXRhLjEiLCJwcm9vZiI6eyJwaV9hIjpbIjIxNTU3NDExOTQ5MDY3MTA0MzM5MjkzNTUwNTI2OTg1MjE1NzMwNzEwMDcwNzI0MjUzOTc4OTM1MzY0NzY1MTUyMTY2NDk1NjAwNTIyIiwiNDEwMjUzODM4NTkwNzM3MzA1MDYyNDQ1ODc1MTYyNTE5NDgxMjU5ODQ4NTk0MTYwOTkxMDY0NjMzNjYyNjMxNDUwMTQ1ODM4NDc3MyIsIjEiXSwicGlfYiI6W1siNTU1OTkyNDE1OTQxMzUyNjAzMjA1NzY5OTMxNjYzNTgxOTAxMTU1MDAzMjgwNTU4ODgwMzMyMjk2Mjk2MDczNTgxNzc0MzI0ODE2MiIsIjY4NjY4ODc1NTgzMjA1ODE4MDg0OTI0ODc5NDUzMTY2NjI0NzIyODAwMzkxNzYzMTYxODc0MDM2MzU3NjU3Nzg2Nzc4MTcxNjY4ODIiXSxbIjE2OTUzNjU2ODQwNjg4NDMxMzg3ODk4NzE1ODY4MTA0ODEwMTEzMDUwMDQ1NDg1OTQ5Mzk0NjIzMDAwNjkzNTYwOTExODA2NjMxODYiLCIxNjI4NTgxMzMzMzU4NzU5NjA1NDE5Mjk3NDEyMDA4MTE0OTQ4MDk1MTIwMDcwMjg0NjYwNjI4NzIxMDE1ODM1NjI2MTg3ODg0MzI0Il0sWyIxIiwiMCJdXSwicGlfYyI6WyI4ODYwMzk5MTk2ODcxMDY0MTAxOTA5NTMxNDE4MzU4MTU3NTExMzQ4OTA0NjQ1Mzc1MjE2MjQ3NDY3MjA2MDcyMDc5Nzc5MjYwMjMiLCI0MjM5MzIzMDA4MjIxMjA3MzY4NjcwMDk5NDg2MTk1MTI1MDEwMTM3NDIyMjkxMTQyNjczODQ4OTE2NTk3NjI5ODk2NzMyNjI2NTE4IiwiMSJdLCJwcm90b2NvbCI6Imdyb3RoMTYiLCJjdXJ2ZSI6ImJuMTI4In0sInB1Yl9zaWduYWxzIjpbIjAiLCIyMTU3NTEyNzIxNjIzNjI0ODg2OTcwMjI3NjI0NjAzNzU1NzExOTAwNzQ2NjE4MDMwMTk1Nzc2MjE5NjU5Mzc4NjczMzAwNzYxNyIsIjQ0ODczODYzMzI0Nzk0ODkxNTgwMDM1OTc4NDQ5OTA0ODc5ODQ5MjU0NzE4MTM5MDc0NjI0ODM5MDcwNTQ0MjU3NTk1NjQxNzUzNDEiLCIwIiwiMCIsIjAiLCIxIiwiMSIsIjI1MTk4NTQzMzgxMjAwNjY1NzcwODA1ODE2MDQ2MjcxNTk0ODg1NjA0MDAyNDQ1MTA1NzY3NjUzNjE2ODc4MTY3ODI2ODk1NjE3IiwiMSIsIjQ0ODczODYzMzI0Nzk0ODkxNTgwMDM1OTc4NDQ5OTA0ODc5ODQ5MjU0NzE4MTM5MDc0NjI0ODM5MDcwNTQ0MjU3NTk1NjQxNzUzNDEiLCIxNzI1OTA4NDUxIiwiMTk4Mjg1NzI2NTEwNjg4MjAwMzM1MjA3MjczODM2MTIzMzM4Njk5IiwiMCIsIjMiLCIxIiwiOTkiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIxIiwiMjUxOTg1NDMzODEyMDA2NjU3NzA4MDU4MTYwNDYyNzE1OTQ4ODU2MDQwMDI0NDUxMDU3Njc2NTM2MTY4NzgxNjc4MjY4OTU2MTciLCIwIl19LHsiaWQiOjIsImNpcmN1aXRJZCI6ImxpbmtlZE11bHRpUXVlcnkxMC1iZXRhLjEiLCJwcm9vZiI6eyJwaV9hIjpbIjEzMTc0NTMzOTY0MTA3NjAwODE1NDkzODk0NDMzNzExMTQ5MzE3MzM4Mzg2MjU1MTQ4OTYwNDI1NzQ3MTEwMzYzMDE2NjE3NDY2MDUwIiwiMjU0NTU5ODM3MDkwMjQ4Njc1NzQ5NTcxODE0NTQzNjU1ODg1MTUzMTk4MDk1NTIxNzE1NTYwODM3NjA5Mzk0OTEyMzQ5OTE1MzExNyIsIjEiXSwicGlfYiI6W1siMTgzNTQyNjI4MjcyNTYyOTY3MzIwOTQ5NzU0MDk3Nzc5NTMzNzg5OTA0MDc4NjEzODE2MDk3Nzk5NzI2OTEwOTkwOTQ2MDAxNjQxNDciLCIxMTc5MjMxNTUzNTAzOTM4MTY4NDE3Mjc3NDM3OTgzOTg0NjA1MTY0MjUyOTQzNzA2ODk1NDM4OTQyMTcyNjkzNjE0NzY3OTY4ODkyIl0sWyI1NzYxNzUwMzgwMDM4OTIxNjI1MTYxMTMwMzE4Mjc5Mzk3NTk0NDY5MzU1Mzc1ODE4MzkzNDkwMzEzMjI4OTkyMjAwMjQxOTEyODE2IiwiMTIyOTUwOTg0NTAwOTY2OTk2NTQxMzE1Nzc2ODMzNjY5NzYzMzk3NjE2MTQzNTQ4NDE0NjcxNTEwMTc2MjU4OTg3MzIzOTQ0OTc3ODEiXSxbIjEiLCIwIl1dLCJwaV9jIjpbIjgzMjkzMzU1NjE0NzQwNjE5NDY5MjM3MjE3ODQ3MjA3ODAxNTMzNzMzNjAzMzA0MjkwNzA1MDE1MzY5NDQ0ODQ5MjQ0NTQ0NTgxMDkiLCI3ODA1MjQ4MTU3MDEwNzUyOTMxMzc4NDM5ODE1MDU0ODg0OTM5MzU2Mjc2OTk2Mjk4MTE3NjIyMzY2MzQ5OTczMzc2OTI3NDUxOTk1IiwiMSJdLCJwcm90b2NvbCI6Imdyb3RoMTYiLCJjdXJ2ZSI6ImJuMTI4In0sInB1Yl9zaWduYWxzIjpbIjE3MjIyOTY3NTY3NTM5MzU1MzM4MzM3NDAwNDY5NTM3NTA1NTI0NTk5OTYzMjQwNTg3MzkyNDQxMjUxMzk0Mzc0NTM1MTAxMzA4NjE0IiwiMSIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIxNTU3NzExNDc5OTA1NjkzOTYzMzU1Mjg0NTUzMTAxMTAyNDY3MjkzOTQ5MzQ5Mjc2OTYyODI4NTY2MTM1OTcxMTY1NTIxNDU2MTE2MiIsIjE2OTk4NzYyOTY1Mzk2OTQ0NzgyNjY3NTU3NzQxMTg1ODI4MTM2NDY3NzQ3NzYyODMwMDI4MjE3MDI3OTczNjE3MzczODYyMzAxOTU4IiwiOTMwMjUyNjIwODUwNzc1Mzc5OTUwMTEzMDEyODkwODQ5NDY3MzQxMjQ0MzYzMTU0MTQyNDQwOTU1MTIwNTI3NzUyOTk0OTY2MjM5NCIsIjE0NjEyNTE4MDA2NDkzOTk4MDM3MTQ5Mjk5NjQ3OTc0MjM3NzcxNTUxMDcwMzEyMDk2ODgyNDA3NDQwNjUxMDUyNzUyMjU5MDM4NDAzIiwiMTQ2MTI1MTgwMDY0OTM5OTgwMzcxNDkyOTk2NDc5NzQyMzc3NzE1NTEwNzAzMTIwOTY4ODI0MDc0NDA2NTEwNTI3NTIyNTkwMzg0MDMiLCIxNDYxMjUxODAwNjQ5Mzk5ODAzNzE0OTI5OTY0Nzk3NDIzNzc3MTU1MTA3MDMxMjA5Njg4MjQwNzQ0MDY1MTA1Mjc1MjI1OTAzODQwMyIsIjE0NjEyNTE4MDA2NDkzOTk4MDM3MTQ5Mjk5NjQ3OTc0MjM3NzcxNTUxMDcwMzEyMDk2ODgyNDA3NDQwNjUxMDUyNzUyMjU5MDM4NDAzIiwiMTQ2MTI1MTgwMDY0OTM5OTgwMzcxNDkyOTk2NDc5NzQyMzc3NzE1NTEwNzAzMTIwOTY4ODI0MDc0NDA2NTEwNTI3NTIyNTkwMzg0MDMiLCIxNDYxMjUxODAwNjQ5Mzk5ODAzNzE0OTI5OTY0Nzk3NDIzNzc3MTU1MTA3MDMxMjA5Njg4MjQwNzQ0MDY1MTA1Mjc1MjI1OTAzODQwMyIsIjE0NjEyNTE4MDA2NDkzOTk4MDM3MTQ5Mjk5NjQ3OTc0MjM3NzcxNTUxMDcwMzEyMDk2ODgyNDA3NDQwNjUxMDUyNzUyMjU5MDM4NDAzIl19LHsiaWQiOjMsImNpcmN1aXRJZCI6ImNyZWRlbnRpYWxBdG9taWNRdWVyeVYzLWJldGEuMSIsInByb29mIjp7InBpX2EiOlsiNzMyMjMxODkwOTcyMDAxNzQ5MjgwNzk4ODM3MDc3NzgwMTE3MzM4OTkyMjczNTU0NTMzNDk1OTA5NDg0NTQ2MzM0NjAyODc5NjA3NyIsIjkyNTUzMDA1NTg2OTg3MTExMTUxOTMyOTQ1ODk1OTYzNzY3NzIyNDIwNDA2OTY3NjY0NzQ1OTI4MDExODg2NjMyMDU3NDk2MTkyNTUiLCIxIl0sInBpX2IiOltbIjY1MzE5NDg4MDUxNDg3NTkwNDAyMzQ3Mzk5NjkzNTcxOTE5ODk2NDQwNjY4OTIzMTc5NTAyMjkyNDI3MTY2OTgwNTkxNDE0MzM2MTAiLCIxMzkyNDQxNTYxNDQ5NzcwNTgxMjUyNzk1MDcyNzIzMDUxNTgxOTk5ODk3NTAwMzU5NDgwMzExNzk3OTExNTc5MTIyMTc5NzM3NDk5MiJdLFsiNjk5NjE2Mjk3NTA2NDEzODA2ODY3NTkyNTY3ODgzMzY2NDEwMzk5MjI2NzM2MjU3ODcwOTMyNjQxNzMxMjQ1OTUzNDAxMDI1MjQ5OSIsIjIxNjQ4MDg0NTQ1NDQ3NzM1NjU1NTkxNzI4MzA4MjE5ODA3MDE4ODIxODYyNjU1MDUwMTY3ODAxNTU1OTU5MDYzOTc0MjAyMTcyMDExIl0sWyIxIiwiMCJdXSwicGlfYyI6WyIyMTUwMzU3ODU2ODg1OTIzNTE5NjQyNDk3MjA5NjYzMjUzMDg2Nzg1MzkwNzE0NTc5MDQ1NjEyMDA4OTE3ODkyMDAxMzI4NDIyMjE3IiwiMjg3ODIzMTUzNDU1MzQwNjc5NTA4OTUzMTU0ODQwMTY5NDU5NTU4NzE5NTM0NDk5ODg4NDg4MjQ3OTIzNTMwMTgyMDY2NzAwMTc2NyIsIjEiXSwicHJvdG9jb2wiOiJncm90aDE2IiwiY3VydmUiOiJibjEyOCJ9LCJwdWJfc2lnbmFscyI6WyIxIiwiMjE1NzUxMjcyMTYyMzYyNDg4Njk3MDIyNzYyNDYwMzc1NTcxMTkwMDc0NjYxODAzMDE5NTc3NjIxOTY1OTM3ODY3MzMwMDc2MTciLCI0NDg3Mzg2MzMyNDc5NDg5MTU4MDAzNTk3ODQ0OTkwNDg3OTg0OTI1NDcxODEzOTA3NDYyNDgzOTA3MDU0NDI1NzU5NTY0MTc1MzQxIiwiMTcyMjI5Njc1Njc1MzkzNTUzMzgzMzc0MDA0Njk1Mzc1MDU1MjQ1OTk5NjMyNDA1ODczOTI0NDEyNTEzOTQzNzQ1MzUxMDEzMDg2MTQiLCI1MTEzMTEwNzQyMTYzNTYxMTYxNjgxMTEwNjAwODU4MDE4ODg2NDQwMjkxMjg4OTYyNjgxMjMzNDI5OTc1MTMzOTE2MzQ4NjA2NTIxIiwiMCIsIjEiLCIzIiwiMjUxOTg1NDMzODEyMDA2NjU3NzA4MDU4MTYwNDYyNzE1OTQ4ODU2MDQwMDI0NDUxMDU3Njc2NTM2MTY4NzgxNjc4MjY4OTU2MTciLCIxIiwiNDQ4NzM4NjMzMjQ3OTQ4OTE1ODAwMzU5Nzg0NDk5MDQ4Nzk4NDkyNTQ3MTgxMzkwNzQ2MjQ4MzkwNzA1NDQyNTc1OTU2NDE3NTM0MSIsIjE3MjU5MDg0NzAiLCIyMTk1Nzg2MTcwNjQ1NDAwMTYyMzQxNjE2NDAzNzU3NTU4NjU0MTIiLCIxMjk2MzUxNzU4MjY5MDYxMTczMzE3MTA1MDQxOTY4MDY3MDc3NDUxOTE0Mzg2MDg2MjIyOTMxNTE2MTk5MTk0OTU5ODY5NDYzODgyIiwiMCIsIjEiLCIxNzAyMjUyODAwMDAwMDAwMDAwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMSIsIjI1MTk4NTQzMzgxMjAwNjY1NzcwODA1ODE2MDQ2MjcxNTk0ODg1NjA0MDAyNDQ1MTA1NzY3NjUzNjE2ODc4MTY3ODI2ODk1NjE3IiwiMTIzNDUiXX1dfSwiZnJvbSI6ImRpZDppZGVuMzpwb2x5Z29uOmFtb3k6eDdaOTVWa1V1eW82bXFyYUp3MlZHd0NmcVR6ZHFoTTFSVmpSSHpjcEsiLCJ0byI6ImRpZDppZGVuMzpwb2x5Z29uOmFtb3k6eENScDc1RGdBZFM2M1c2NWZtWEh6NnA5RHdkb251UlU5ZTQ2RGlmaFgifQ.eyJwcm9vZiI6eyJwaV9hIjpbIjEzNjM5ODg3OTk1MTc5MDY4MjQ0MDM0NjMzNDAxODQyMzY0NTc5NzcxMjAwNTg2NDg0MzA3MDI5ODczNjc2MzY4Mjc3MDQ2NTE2MzM2IiwiMzEzMzc3MzM4MzAzNjY5MzI1MTgwNDAyNjMzMzE0NzA3OTI4NTkzNTcyMTU4Mjc0OTU4ODA1Mzg4NDc1Mjg5MzI0OTk1NDgzMjg2NSIsIjEiXSwicGlfYiI6W1siMjEzOTA4NzY1NDA1MjY5MzgzMzUwMjU3NDcxNzU0MjAzOTc4MTg1MjEwMjgyMTY5NjM0OTU0NDE1MjMzMTI0NjE0OTk4Njg5OTAxNTUiLCIxMjU4ODcwNDQwNTExNDUzMjk1NTIyNzYwNTA1MTMyMTI1NTE5NDY1Mjg0ODg3NDkwNjk5NTU1Nzc2OTg0MzEwNjIxNzQzOTc4MDY0NCJdLFsiMTAwMzExNTAyNDU1MTY0NDI3Nzk5NTM0ODQ0NzY5MzUyOTg5ODk0OTAxNjM5OTgzNjQ1NjUyNjI0ODYxMjQzNjg5Mjg1OTMyOTQ1ODciLCI0NTkwMjMxNzU4MTUyNDM2NTAwODM2MjU1MjEwMzc3MjY3OTI4NzM5NjI5NzQ3MjM5OTg5MTU4NzEwNDQ3MTI3MDQ0MTk5OTg2NDc1Il0sWyIxIiwiMCJdXSwicGlfYyI6WyIxNTMwNDA4MjA0OTY0NzE2MTU5NzA3NjA1MjAyNDU0ODE5NDg2NzI0NDE1NDE4NzIyNzc1NzQwOTE0MDQ2MTk0NTY4NjA1MTAzMDc1MCIsIjEwMzM2ODM1MjU1OTU5Nzc0NjI1MjY4NjU0Mzc4Mzk1Mzc4MTUxNzE5MzI0Mzg0NDk4NDE3MzY5MzY2MTM2NDgzODA5MjQ2MDE3OTcxIiwiMSJdLCJwcm90b2NvbCI6Imdyb3RoMTYiLCJjdXJ2ZSI6ImJuMTI4In0sInB1Yl9zaWduYWxzIjpbIjIxNTc1MTI3MjE2MjM2MjQ4ODY5NzAyMjc2MjQ2MDM3NTU3MTE5MDA3NDY2MTgwMzAxOTU3NzYyMTk2NTkzNzg2NzMzMDA3NjE3IiwiNTY5ODk5ODc0MTY1NzM3NDM4NjIxMjUwNzAyNzA5OTczNTkzMzYxMzI5ODYwNzg3ODIzNzQ4MjMwODU5NDIxODgxODQ2MjQ3ODA2MiIsIjE3ODQ5OTgxNzIwNjM0MjEyODAyNjY0MTg5OTI5NjcxMTQwNzYyNTU3NDgzMjM2NzA4MDk3NzIzODg0MTcyNjQxMTI0NjkxMjIyMjk4Il19'; 85 | 86 | const verifier = await Verifier.newVerifier({ 87 | stateResolver: resolvers, 88 | circuitsDir: getTestDataPath('./testdata'), 89 | documentLoader: schemaLoader 90 | }); 91 | 92 | await verifier.fullVerify(tokenString, authRequest, testOpts); 93 | }); 94 | 95 | it('should verify successfully with optional=true in first scope', async () => { 96 | const authRequest: AuthorizationRequestMessage = { 97 | id: 'f5bcdfc9-3819-4052-ad97-c059119e563c', 98 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 99 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, 100 | thid: 'f5bcdfc9-3819-4052-ad97-c059119e563c', 101 | from: 'did:iden3:polygon:amoy:xCRp75DgAdS63W65fmXHz6p9DwdonuRU9e46DifhX', 102 | body: { 103 | callbackUrl: 'http://localhost:8080/callback?id=1234442-123123-123123', 104 | reason: 'reason', 105 | message: 'message', 106 | scope: [ 107 | { 108 | id: 1, 109 | circuitId: CircuitId.AtomicQueryV3, 110 | optional: true, 111 | query: { 112 | proofType: ProofType.BJJSignature, 113 | allowedIssuers: ['*'], 114 | type: 'KYCAgeCredential', 115 | context: 116 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-nonmerklized.jsonld', 117 | credentialSubject: { 118 | documentType: { 119 | $eq: 99 120 | } 121 | } 122 | } 123 | }, 124 | { 125 | id: 2, 126 | circuitId: CircuitId.LinkedMultiQuery10, 127 | optional: false, 128 | query: { 129 | groupId: 1, 130 | proofType: ProofType.Iden3SparseMerkleTreeProof, 131 | allowedIssuers: ['*'], 132 | type: 'KYCEmployee', 133 | context: 134 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v101.json-ld', 135 | credentialSubject: { 136 | documentType: { 137 | $eq: 1 138 | }, 139 | position: { 140 | $eq: 'boss', 141 | $ne: 'employee' 142 | } 143 | } 144 | } 145 | }, 146 | { 147 | id: 3, 148 | circuitId: CircuitId.AtomicQueryV3, 149 | optional: false, 150 | params: { 151 | nullifierSessionId: '12345' 152 | }, 153 | query: { 154 | groupId: 1, 155 | proofType: ProofType.BJJSignature, 156 | allowedIssuers: ['*'], 157 | type: 'KYCEmployee', 158 | context: 159 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v101.json-ld', 160 | credentialSubject: { 161 | hireDate: { 162 | $eq: '2023-12-11' 163 | } 164 | } 165 | } 166 | } 167 | ] 168 | } 169 | }; 170 | 171 | const tokenString = 172 | 'eyJhbGciOiJncm90aDE2IiwiY2lyY3VpdElkIjoiYXV0aFYyIiwiY3JpdCI6WyJjaXJjdWl0SWQiXSwidHlwIjoiYXBwbGljYXRpb24vaWRlbjMtemtwLWpzb24ifQ.eyJpZCI6ImIyZGY2MjYyLTM2ZTktNDgzYi1iZjNjLTRkZjUwYmRjZDE5MyIsInR5cCI6ImFwcGxpY2F0aW9uL2lkZW4zLXprcC1qc29uIiwidHlwZSI6Imh0dHBzOi8vaWRlbjMtY29tbXVuaWNhdGlvbi5pby9hdXRob3JpemF0aW9uLzEuMC9yZXNwb25zZSIsInRoaWQiOiIwOTRiM2ZjNS00YmNiLTQ2OTEtYWY3OS1jNWVmMDE5YTI1YWIiLCJib2R5Ijp7Im1lc3NhZ2UiOiJtZXNzYWdlIiwic2NvcGUiOlt7ImlkIjoyLCJjaXJjdWl0SWQiOiJsaW5rZWRNdWx0aVF1ZXJ5MTAtYmV0YS4xIiwicHJvb2YiOnsicGlfYSI6WyIxNTUxMDY1ODgzNDY0Njk3NDQyMzE3NTYzMTY0OTQ3MTg3ODU0NTIzMTYxNDUzMDA3NTYwOTI4NTU2MTczNDI5NDg1MDUzMzY1Njc0NCIsIjc0NTc2NTYyODQxODI1Mzk3MzgzNDQ2MDU3NjczMTkxNjc1NjA1NTk3MDg5NjUyMDI2MDk5MjkxNTE4MDA0MDkyNTQ3NDE1Njk0ODciLCIxIl0sInBpX2IiOltbIjEyNzQ2NTA4NDE0MzE2ODE0ODQyMDI3MDg2OTAyOTA4NTk4NTY3NTE2NTQ5NjE3MjM0NDMyOTA2OTE1NDM5MzEzMzk4MzM4NjQyODQwIiwiMTg2MDA5MDIyMjY5OTI0ODU0NTE5ODUxNzc3MDAxODQ4MDc3ODM5NTIwMzQwNTU0MzA2Njc0MDI5MzgxNjgyOTQyNDQwNzA4MjUzMjkiXSxbIjEyNzQ0MzgxNTg2OTkxMzQwNzIxNjAyODM5MjA2MTAzMzg4NjgxMjU1ODI4NzQyNzEzMDQyNTA2Mzc5MTA0MzA0MjA0OTA2NTQzOTAiLCIzMTA4ODQyNTgxOTYxOTA4MTk4MTYyMjE1MTE1MjQxOTcwMjU1MDI4NzM1NjQ5MjYyMDk4NDIyNTI4NDA5NDk1MjY1MjQ1MjE0ODEyIl0sWyIxIiwiMCJdXSwicGlfYyI6WyI4MjQ5ODYxNzkwNTEyNzg0NDQyMTA2ODg1OTA2NDc4NDI2NjY5MTU2NjU1OTcxNDc2MzIzMzcwNTMzNTEyNzcxMTA4ODE3MTU5NjA0IiwiMTMyODM4NDMzMTExOTcxNzg5NDc2MDQ1NzQ1NTQzODE4NTUzOTUzNTg1NzcxMjYyMzAyODg0NTk5NDUxMjkwNTY5OTcwNzMxOTgxOTgiLCIxIl0sInByb3RvY29sIjoiZ3JvdGgxNiIsImN1cnZlIjoiYm4xMjgifSwicHViX3NpZ25hbHMiOlsiNTAwOTg5MTMxMjg0MDA2NzI0ODkwOTk4NjUzOTYxODg4NzAwNjQ0Njg2MzE2NTY2MzI5ODE3NDQ0NTQ3ODQ4OTUwNDgwNzQ2NjgxNiIsIjEiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMTU1NzcxMTQ3OTkwNTY5Mzk2MzM1NTI4NDU1MzEwMTEwMjQ2NzI5Mzk0OTM0OTI3Njk2MjgyODU2NjEzNTk3MTE2NTUyMTQ1NjExNjIiLCIxNjk5ODc2Mjk2NTM5Njk0NDc4MjY2NzU1Nzc0MTE4NTgyODEzNjQ2Nzc0Nzc2MjgzMDAyODIxNzAyNzk3MzYxNzM3Mzg2MjMwMTk1OCIsIjkzMDI1MjYyMDg1MDc3NTM3OTk1MDExMzAxMjg5MDg0OTQ2NzM0MTI0NDM2MzE1NDE0MjQ0MDk1NTEyMDUyNzc1Mjk5NDk2NjIzOTQiLCIxNDYxMjUxODAwNjQ5Mzk5ODAzNzE0OTI5OTY0Nzk3NDIzNzc3MTU1MTA3MDMxMjA5Njg4MjQwNzQ0MDY1MTA1Mjc1MjI1OTAzODQwMyIsIjE0NjEyNTE4MDA2NDkzOTk4MDM3MTQ5Mjk5NjQ3OTc0MjM3NzcxNTUxMDcwMzEyMDk2ODgyNDA3NDQwNjUxMDUyNzUyMjU5MDM4NDAzIiwiMTQ2MTI1MTgwMDY0OTM5OTgwMzcxNDkyOTk2NDc5NzQyMzc3NzE1NTEwNzAzMTIwOTY4ODI0MDc0NDA2NTEwNTI3NTIyNTkwMzg0MDMiLCIxNDYxMjUxODAwNjQ5Mzk5ODAzNzE0OTI5OTY0Nzk3NDIzNzc3MTU1MTA3MDMxMjA5Njg4MjQwNzQ0MDY1MTA1Mjc1MjI1OTAzODQwMyIsIjE0NjEyNTE4MDA2NDkzOTk4MDM3MTQ5Mjk5NjQ3OTc0MjM3NzcxNTUxMDcwMzEyMDk2ODgyNDA3NDQwNjUxMDUyNzUyMjU5MDM4NDAzIiwiMTQ2MTI1MTgwMDY0OTM5OTgwMzcxNDkyOTk2NDc5NzQyMzc3NzE1NTEwNzAzMTIwOTY4ODI0MDc0NDA2NTEwNTI3NTIyNTkwMzg0MDMiLCIxNDYxMjUxODAwNjQ5Mzk5ODAzNzE0OTI5OTY0Nzk3NDIzNzc3MTU1MTA3MDMxMjA5Njg4MjQwNzQ0MDY1MTA1Mjc1MjI1OTAzODQwMyJdfSx7ImlkIjozLCJjaXJjdWl0SWQiOiJjcmVkZW50aWFsQXRvbWljUXVlcnlWMy1iZXRhLjEiLCJwcm9vZiI6eyJwaV9hIjpbIjEyMjAwOTE0OTY1NjE3Mjk4NDUwMTI3MzY0OTA3Njk5MjE1NzM1MjYzMDAxMTE4MzY4MDUzMzQ0OTg1MzQ4NDU5NTg5OTkwMDQ0MDE3IiwiMTk0MzI4MDMzNzk4MDg4NDgxOTk4MjE2OTEwNDgzNzE4NDEyMzMyMzc4OTA2ODMyNzM3ODgyNjc0OTY2NTg0NDI1ODg1OTk0NjMxNzYiLCIxIl0sInBpX2IiOltbIjk0MzA2ODQxMDY5NTMwMzQ4OTk0ODQ3ODQzNDE5NDcyMTc4OTU2ODk2NjQwMDQ2MTI5MzU1MTIxMTY0NzQwNTUxMDI3MTc3MzAyMTUiLCIxMDI4NDQxNTM2MDg3NDQzMjgxNjM4MjEyNjk0MDA0MjYxNjIxNTkzMzMwMjg2OTI1NDk5OTcyMDA1NTg0NDk0ODkwNDMyMTc5NDIwIl0sWyIyNTk3NDE5OTg5MTk3NDA2NTQ5NDQ1ODY1MTg3MTg0MjE2ODE1OTU5MDE4ODYxMzUyMDQ2MzAxNzAzMTYzNjAwODYxNTI3NjYzMjc4IiwiMTYzODU5MDk5MjcyOTUwODEwMjY4NjYyNjI0Nzk3NzQ0NTg4MDEzMTU1NzYxNTI1MTgzOTQzNzk0NjExODAxODg3MzQyODQyNjM4NjkiXSxbIjEiLCIwIl1dLCJwaV9jIjpbIjU3NTY2Nzc5MzE2ODU2NTY1ODUwNzA5OTg3MjM3NTIzNTY5OTEzNjQyMzAxODA0Nzg5NDU0NzQ5ODIyNjg5NTM0Nzk4MDA1MjQyMDciLCIyMDMwNzY5NTk1MTcwODc3MTk2MjIxNzYxMjc2MjM2NzY3ODgzNjg1OTg0OTYwNTY2OTgzODUxMDUxMTg5MjM5MDQ3NTkyMzk4OTk2OSIsIjEiXSwicHJvdG9jb2wiOiJncm90aDE2IiwiY3VydmUiOiJibjEyOCJ9LCJwdWJfc2lnbmFscyI6WyIxIiwiMjE1NzUxMjcyMTYyMzYyNDg4Njk3MDIyNzYyNDYwMzc1NTcxMTkwMDc0NjYxODAzMDE5NTc3NjIxOTY1OTM3ODY3MzMwMDc2MTciLCI0NDg3Mzg2MzMyNDc5NDg5MTU4MDAzNTk3ODQ0OTkwNDg3OTg0OTI1NDcxODEzOTA3NDYyNDgzOTA3MDU0NDI1NzU5NTY0MTc1MzQxIiwiNTAwOTg5MTMxMjg0MDA2NzI0ODkwOTk4NjUzOTYxODg4NzAwNjQ0Njg2MzE2NTY2MzI5ODE3NDQ0NTQ3ODQ4OTUwNDgwNzQ2NjgxNiIsIjUxMTMxMTA3NDIxNjM1NjExNjE2ODExMTA2MDA4NTgwMTg4ODY0NDAyOTEyODg5NjI2ODEyMzM0Mjk5NzUxMzM5MTYzNDg2MDY1MjEiLCIwIiwiMSIsIjMiLCIyNTE5ODU0MzM4MTIwMDY2NTc3MDgwNTgxNjA0NjI3MTU5NDg4NTYwNDAwMjQ0NTEwNTc2NzY1MzYxNjg3ODE2NzgyNjg5NTYxNyIsIjEiLCI0NDg3Mzg2MzMyNDc5NDg5MTU4MDAzNTk3ODQ0OTkwNDg3OTg0OTI1NDcxODEzOTA3NDYyNDgzOTA3MDU0NDI1NzU5NTY0MTc1MzQxIiwiMTc1MzM1NTM5NyIsIjIxOTU3ODYxNzA2NDU0MDAxNjIzNDE2MTY0MDM3NTc1NTg2NTQxMiIsIjEyOTYzNTE3NTgyNjkwNjExNzMzMTcxMDUwNDE5NjgwNjcwNzc0NTE5MTQzODYwODYyMjI5MzE1MTYxOTkxOTQ5NTk4Njk0NjM4ODIiLCIwIiwiMSIsIjE3MDIyNTI4MDAwMDAwMDAwMDAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIxIiwiMjUxOTg1NDMzODEyMDA2NjU3NzA4MDU4MTYwNDYyNzE1OTQ4ODU2MDQwMDI0NDUxMDU3Njc2NTM2MTY4NzgxNjc4MjY4OTU2MTciLCIxMjM0NSJdfV19LCJmcm9tIjoiZGlkOmlkZW4zOnBvbHlnb246YW1veTp4N1o5NVZrVXV5bzZtcXJhSncyVkd3Q2ZxVHpkcWhNMVJWalJIemNwSyIsInRvIjoiZGlkOmlkZW4zOnBvbHlnb246YW1veTp4Q1JwNzVEZ0FkUzYzVzY1Zm1YSHo2cDlEd2RvbnVSVTllNDZEaWZoWCJ9.eyJwcm9vZiI6eyJwaV9hIjpbIjE0ODA1MzI2NDU2MzIzOTAzOTA1MDk1NjczMDk0NDMzMzQzMTEzNTE1NDY0OTkxMzU4OTk1ODY5MjY2MDI1MTg1NTMyNjEzMTQ3NDA1IiwiMTIwNzkwNTA2MjM0MzM0NTMyODE5MzI3MjMyMTExNjc0NjM2MDQwNDMzMzE5NTMxMDcwNDY1NDM3NTkxMDg4MDIyNjQ4NjY4NTc1ODAiLCIxIl0sInBpX2IiOltbIjM1NjIyODQ1MjI3MDUzNTA1OTA3Mzk2OTI2MzI3MTM3NjY2MjAzNjc3MzAxMDQzMzM0ODMzNzIzMjYxNjQwMjM0NDQyNTE2MTkwMzMiLCIyMDU1MTU4MDg5NTQyMTgyODQ0NjQ2MzczODU2Nzk1MTE5NDkxNTgyNzM3NjEzNjczNDE1MDQwMTkzNjMxMzQxNjIwMzAwOTYwMTA4MyJdLFsiMTYyMjEyMzExMjgxNTg1MzQ5NDY2MDQ4NDAxMDI4Njg2OTYxNzIwNDY3NTk4OTkyNTczNDE1MjIzMjMzNjcwMTEwNTIxOTIzOTEzNjkiLCIyMTQ5NzI5NDAyNzE2OTc1MDY5NzA2Njk4MTcyMzczNDc1ODk0MzczNzY1NDA4MDMyODQ3NzAzNDQ1OTQ4NjYwNzYxNTY1MzY2Njk5OSJdLFsiMSIsIjAiXV0sInBpX2MiOlsiODYyMzEzMjQxNzI4MjE3NzUwNTY1MDg4MDYzMTE0OTg4Nzc0Mzg1ODkyMzkyMjI5NzY1NDExMTAxMzEyMDM0MTg4ODUwMzE0MDU3MCIsIjU1NzAzMzk0NjgzNjk3Njg4MjkxMjMzNTAzNTA2ODE5OTEyMTkxMzk3MjMxMDY3MTYwODQwODMzNDIzMTc3Mzg5NzUzOTMwNzM2NzciLCIxIl0sInByb3RvY29sIjoiZ3JvdGgxNiIsImN1cnZlIjoiYm4xMjgifSwicHViX3NpZ25hbHMiOlsiMjE1NzUxMjcyMTYyMzYyNDg4Njk3MDIyNzYyNDYwMzc1NTcxMTkwMDc0NjYxODAzMDE5NTc3NjIxOTY1OTM3ODY3MzMwMDc2MTciLCIyMTU1OTkxMjE3NjQ3NjY0NTQ4MjQ3MjE4Mjc0ODgyMDY4MzcwOTM5NTk3Nzc0NDU3MzY3MTIzNDg2NjM1ODIxOTE3NDUxODQyNDQyMCIsIjE3ODQ5OTgxNzIwNjM0MjEyODAyNjY0MTg5OTI5NjcxMTQwNzYyNTU3NDgzMjM2NzA4MDk3NzIzODg0MTcyNjQxMTI0NjkxMjIyMjk4Il19'; 173 | 174 | const verifier = await Verifier.newVerifier({ 175 | stateResolver: resolvers, 176 | circuitsDir: getTestDataPath('./testdata'), 177 | documentLoader: schemaLoader 178 | }); 179 | 180 | await verifier.fullVerify(tokenString, authRequest, testOpts); 181 | }); 182 | }); 183 | -------------------------------------------------------------------------------- /src/state/types/ethers-contracts/factories/Abi__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from 'ethers'; 6 | import type { Provider } from '@ethersproject/providers'; 7 | import type { Abi, AbiInterface } from '../Abi'; 8 | 9 | const _abi = [ 10 | { 11 | anonymous: false, 12 | inputs: [ 13 | { 14 | indexed: false, 15 | internalType: 'uint8', 16 | name: 'version', 17 | type: 'uint8' 18 | } 19 | ], 20 | name: 'Initialized', 21 | type: 'event' 22 | }, 23 | { 24 | anonymous: false, 25 | inputs: [ 26 | { 27 | indexed: true, 28 | internalType: 'address', 29 | name: 'previousOwner', 30 | type: 'address' 31 | }, 32 | { 33 | indexed: true, 34 | internalType: 'address', 35 | name: 'newOwner', 36 | type: 'address' 37 | } 38 | ], 39 | name: 'OwnershipTransferStarted', 40 | type: 'event' 41 | }, 42 | { 43 | anonymous: false, 44 | inputs: [ 45 | { 46 | indexed: true, 47 | internalType: 'address', 48 | name: 'previousOwner', 49 | type: 'address' 50 | }, 51 | { 52 | indexed: true, 53 | internalType: 'address', 54 | name: 'newOwner', 55 | type: 'address' 56 | } 57 | ], 58 | name: 'OwnershipTransferred', 59 | type: 'event' 60 | }, 61 | { 62 | anonymous: false, 63 | inputs: [ 64 | { 65 | indexed: false, 66 | internalType: 'uint256', 67 | name: 'id', 68 | type: 'uint256' 69 | }, 70 | { 71 | indexed: false, 72 | internalType: 'uint256', 73 | name: 'blockN', 74 | type: 'uint256' 75 | }, 76 | { 77 | indexed: false, 78 | internalType: 'uint256', 79 | name: 'timestamp', 80 | type: 'uint256' 81 | }, 82 | { 83 | indexed: false, 84 | internalType: 'uint256', 85 | name: 'state', 86 | type: 'uint256' 87 | } 88 | ], 89 | name: 'StateUpdated', 90 | type: 'event' 91 | }, 92 | { 93 | inputs: [], 94 | name: 'VERSION', 95 | outputs: [ 96 | { 97 | internalType: 'string', 98 | name: '', 99 | type: 'string' 100 | } 101 | ], 102 | stateMutability: 'view', 103 | type: 'function' 104 | }, 105 | { 106 | inputs: [], 107 | name: 'acceptOwnership', 108 | outputs: [], 109 | stateMutability: 'nonpayable', 110 | type: 'function' 111 | }, 112 | { 113 | inputs: [ 114 | { 115 | internalType: 'uint256', 116 | name: 'id', 117 | type: 'uint256' 118 | } 119 | ], 120 | name: 'getGISTProof', 121 | outputs: [ 122 | { 123 | components: [ 124 | { 125 | internalType: 'uint256', 126 | name: 'root', 127 | type: 'uint256' 128 | }, 129 | { 130 | internalType: 'bool', 131 | name: 'existence', 132 | type: 'bool' 133 | }, 134 | { 135 | internalType: 'uint256[64]', 136 | name: 'siblings', 137 | type: 'uint256[64]' 138 | }, 139 | { 140 | internalType: 'uint256', 141 | name: 'index', 142 | type: 'uint256' 143 | }, 144 | { 145 | internalType: 'uint256', 146 | name: 'value', 147 | type: 'uint256' 148 | }, 149 | { 150 | internalType: 'bool', 151 | name: 'auxExistence', 152 | type: 'bool' 153 | }, 154 | { 155 | internalType: 'uint256', 156 | name: 'auxIndex', 157 | type: 'uint256' 158 | }, 159 | { 160 | internalType: 'uint256', 161 | name: 'auxValue', 162 | type: 'uint256' 163 | } 164 | ], 165 | internalType: 'struct IState.GistProof', 166 | name: '', 167 | type: 'tuple' 168 | } 169 | ], 170 | stateMutability: 'view', 171 | type: 'function' 172 | }, 173 | { 174 | inputs: [ 175 | { 176 | internalType: 'uint256', 177 | name: 'id', 178 | type: 'uint256' 179 | }, 180 | { 181 | internalType: 'uint256', 182 | name: 'blockNumber', 183 | type: 'uint256' 184 | } 185 | ], 186 | name: 'getGISTProofByBlock', 187 | outputs: [ 188 | { 189 | components: [ 190 | { 191 | internalType: 'uint256', 192 | name: 'root', 193 | type: 'uint256' 194 | }, 195 | { 196 | internalType: 'bool', 197 | name: 'existence', 198 | type: 'bool' 199 | }, 200 | { 201 | internalType: 'uint256[64]', 202 | name: 'siblings', 203 | type: 'uint256[64]' 204 | }, 205 | { 206 | internalType: 'uint256', 207 | name: 'index', 208 | type: 'uint256' 209 | }, 210 | { 211 | internalType: 'uint256', 212 | name: 'value', 213 | type: 'uint256' 214 | }, 215 | { 216 | internalType: 'bool', 217 | name: 'auxExistence', 218 | type: 'bool' 219 | }, 220 | { 221 | internalType: 'uint256', 222 | name: 'auxIndex', 223 | type: 'uint256' 224 | }, 225 | { 226 | internalType: 'uint256', 227 | name: 'auxValue', 228 | type: 'uint256' 229 | } 230 | ], 231 | internalType: 'struct IState.GistProof', 232 | name: '', 233 | type: 'tuple' 234 | } 235 | ], 236 | stateMutability: 'view', 237 | type: 'function' 238 | }, 239 | { 240 | inputs: [ 241 | { 242 | internalType: 'uint256', 243 | name: 'id', 244 | type: 'uint256' 245 | }, 246 | { 247 | internalType: 'uint256', 248 | name: 'root', 249 | type: 'uint256' 250 | } 251 | ], 252 | name: 'getGISTProofByRoot', 253 | outputs: [ 254 | { 255 | components: [ 256 | { 257 | internalType: 'uint256', 258 | name: 'root', 259 | type: 'uint256' 260 | }, 261 | { 262 | internalType: 'bool', 263 | name: 'existence', 264 | type: 'bool' 265 | }, 266 | { 267 | internalType: 'uint256[64]', 268 | name: 'siblings', 269 | type: 'uint256[64]' 270 | }, 271 | { 272 | internalType: 'uint256', 273 | name: 'index', 274 | type: 'uint256' 275 | }, 276 | { 277 | internalType: 'uint256', 278 | name: 'value', 279 | type: 'uint256' 280 | }, 281 | { 282 | internalType: 'bool', 283 | name: 'auxExistence', 284 | type: 'bool' 285 | }, 286 | { 287 | internalType: 'uint256', 288 | name: 'auxIndex', 289 | type: 'uint256' 290 | }, 291 | { 292 | internalType: 'uint256', 293 | name: 'auxValue', 294 | type: 'uint256' 295 | } 296 | ], 297 | internalType: 'struct IState.GistProof', 298 | name: '', 299 | type: 'tuple' 300 | } 301 | ], 302 | stateMutability: 'view', 303 | type: 'function' 304 | }, 305 | { 306 | inputs: [ 307 | { 308 | internalType: 'uint256', 309 | name: 'id', 310 | type: 'uint256' 311 | }, 312 | { 313 | internalType: 'uint256', 314 | name: 'timestamp', 315 | type: 'uint256' 316 | } 317 | ], 318 | name: 'getGISTProofByTime', 319 | outputs: [ 320 | { 321 | components: [ 322 | { 323 | internalType: 'uint256', 324 | name: 'root', 325 | type: 'uint256' 326 | }, 327 | { 328 | internalType: 'bool', 329 | name: 'existence', 330 | type: 'bool' 331 | }, 332 | { 333 | internalType: 'uint256[64]', 334 | name: 'siblings', 335 | type: 'uint256[64]' 336 | }, 337 | { 338 | internalType: 'uint256', 339 | name: 'index', 340 | type: 'uint256' 341 | }, 342 | { 343 | internalType: 'uint256', 344 | name: 'value', 345 | type: 'uint256' 346 | }, 347 | { 348 | internalType: 'bool', 349 | name: 'auxExistence', 350 | type: 'bool' 351 | }, 352 | { 353 | internalType: 'uint256', 354 | name: 'auxIndex', 355 | type: 'uint256' 356 | }, 357 | { 358 | internalType: 'uint256', 359 | name: 'auxValue', 360 | type: 'uint256' 361 | } 362 | ], 363 | internalType: 'struct IState.GistProof', 364 | name: '', 365 | type: 'tuple' 366 | } 367 | ], 368 | stateMutability: 'view', 369 | type: 'function' 370 | }, 371 | { 372 | inputs: [], 373 | name: 'getGISTRoot', 374 | outputs: [ 375 | { 376 | internalType: 'uint256', 377 | name: '', 378 | type: 'uint256' 379 | } 380 | ], 381 | stateMutability: 'view', 382 | type: 'function' 383 | }, 384 | { 385 | inputs: [ 386 | { 387 | internalType: 'uint256', 388 | name: 'start', 389 | type: 'uint256' 390 | }, 391 | { 392 | internalType: 'uint256', 393 | name: 'length', 394 | type: 'uint256' 395 | } 396 | ], 397 | name: 'getGISTRootHistory', 398 | outputs: [ 399 | { 400 | components: [ 401 | { 402 | internalType: 'uint256', 403 | name: 'root', 404 | type: 'uint256' 405 | }, 406 | { 407 | internalType: 'uint256', 408 | name: 'replacedByRoot', 409 | type: 'uint256' 410 | }, 411 | { 412 | internalType: 'uint256', 413 | name: 'createdAtTimestamp', 414 | type: 'uint256' 415 | }, 416 | { 417 | internalType: 'uint256', 418 | name: 'replacedAtTimestamp', 419 | type: 'uint256' 420 | }, 421 | { 422 | internalType: 'uint256', 423 | name: 'createdAtBlock', 424 | type: 'uint256' 425 | }, 426 | { 427 | internalType: 'uint256', 428 | name: 'replacedAtBlock', 429 | type: 'uint256' 430 | } 431 | ], 432 | internalType: 'struct IState.GistRootInfo[]', 433 | name: '', 434 | type: 'tuple[]' 435 | } 436 | ], 437 | stateMutability: 'view', 438 | type: 'function' 439 | }, 440 | { 441 | inputs: [], 442 | name: 'getGISTRootHistoryLength', 443 | outputs: [ 444 | { 445 | internalType: 'uint256', 446 | name: '', 447 | type: 'uint256' 448 | } 449 | ], 450 | stateMutability: 'view', 451 | type: 'function' 452 | }, 453 | { 454 | inputs: [ 455 | { 456 | internalType: 'uint256', 457 | name: 'root', 458 | type: 'uint256' 459 | } 460 | ], 461 | name: 'getGISTRootInfo', 462 | outputs: [ 463 | { 464 | components: [ 465 | { 466 | internalType: 'uint256', 467 | name: 'root', 468 | type: 'uint256' 469 | }, 470 | { 471 | internalType: 'uint256', 472 | name: 'replacedByRoot', 473 | type: 'uint256' 474 | }, 475 | { 476 | internalType: 'uint256', 477 | name: 'createdAtTimestamp', 478 | type: 'uint256' 479 | }, 480 | { 481 | internalType: 'uint256', 482 | name: 'replacedAtTimestamp', 483 | type: 'uint256' 484 | }, 485 | { 486 | internalType: 'uint256', 487 | name: 'createdAtBlock', 488 | type: 'uint256' 489 | }, 490 | { 491 | internalType: 'uint256', 492 | name: 'replacedAtBlock', 493 | type: 'uint256' 494 | } 495 | ], 496 | internalType: 'struct IState.GistRootInfo', 497 | name: '', 498 | type: 'tuple' 499 | } 500 | ], 501 | stateMutability: 'view', 502 | type: 'function' 503 | }, 504 | { 505 | inputs: [ 506 | { 507 | internalType: 'uint256', 508 | name: 'blockNumber', 509 | type: 'uint256' 510 | } 511 | ], 512 | name: 'getGISTRootInfoByBlock', 513 | outputs: [ 514 | { 515 | components: [ 516 | { 517 | internalType: 'uint256', 518 | name: 'root', 519 | type: 'uint256' 520 | }, 521 | { 522 | internalType: 'uint256', 523 | name: 'replacedByRoot', 524 | type: 'uint256' 525 | }, 526 | { 527 | internalType: 'uint256', 528 | name: 'createdAtTimestamp', 529 | type: 'uint256' 530 | }, 531 | { 532 | internalType: 'uint256', 533 | name: 'replacedAtTimestamp', 534 | type: 'uint256' 535 | }, 536 | { 537 | internalType: 'uint256', 538 | name: 'createdAtBlock', 539 | type: 'uint256' 540 | }, 541 | { 542 | internalType: 'uint256', 543 | name: 'replacedAtBlock', 544 | type: 'uint256' 545 | } 546 | ], 547 | internalType: 'struct IState.GistRootInfo', 548 | name: '', 549 | type: 'tuple' 550 | } 551 | ], 552 | stateMutability: 'view', 553 | type: 'function' 554 | }, 555 | { 556 | inputs: [ 557 | { 558 | internalType: 'uint256', 559 | name: 'timestamp', 560 | type: 'uint256' 561 | } 562 | ], 563 | name: 'getGISTRootInfoByTime', 564 | outputs: [ 565 | { 566 | components: [ 567 | { 568 | internalType: 'uint256', 569 | name: 'root', 570 | type: 'uint256' 571 | }, 572 | { 573 | internalType: 'uint256', 574 | name: 'replacedByRoot', 575 | type: 'uint256' 576 | }, 577 | { 578 | internalType: 'uint256', 579 | name: 'createdAtTimestamp', 580 | type: 'uint256' 581 | }, 582 | { 583 | internalType: 'uint256', 584 | name: 'replacedAtTimestamp', 585 | type: 'uint256' 586 | }, 587 | { 588 | internalType: 'uint256', 589 | name: 'createdAtBlock', 590 | type: 'uint256' 591 | }, 592 | { 593 | internalType: 'uint256', 594 | name: 'replacedAtBlock', 595 | type: 'uint256' 596 | } 597 | ], 598 | internalType: 'struct IState.GistRootInfo', 599 | name: '', 600 | type: 'tuple' 601 | } 602 | ], 603 | stateMutability: 'view', 604 | type: 'function' 605 | }, 606 | { 607 | inputs: [ 608 | { 609 | internalType: 'uint256', 610 | name: 'id', 611 | type: 'uint256' 612 | } 613 | ], 614 | name: 'getStateInfoById', 615 | outputs: [ 616 | { 617 | components: [ 618 | { 619 | internalType: 'uint256', 620 | name: 'id', 621 | type: 'uint256' 622 | }, 623 | { 624 | internalType: 'uint256', 625 | name: 'state', 626 | type: 'uint256' 627 | }, 628 | { 629 | internalType: 'uint256', 630 | name: 'replacedByState', 631 | type: 'uint256' 632 | }, 633 | { 634 | internalType: 'uint256', 635 | name: 'createdAtTimestamp', 636 | type: 'uint256' 637 | }, 638 | { 639 | internalType: 'uint256', 640 | name: 'replacedAtTimestamp', 641 | type: 'uint256' 642 | }, 643 | { 644 | internalType: 'uint256', 645 | name: 'createdAtBlock', 646 | type: 'uint256' 647 | }, 648 | { 649 | internalType: 'uint256', 650 | name: 'replacedAtBlock', 651 | type: 'uint256' 652 | } 653 | ], 654 | internalType: 'struct IState.StateInfo', 655 | name: '', 656 | type: 'tuple' 657 | } 658 | ], 659 | stateMutability: 'view', 660 | type: 'function' 661 | }, 662 | { 663 | inputs: [ 664 | { 665 | internalType: 'uint256', 666 | name: 'id', 667 | type: 'uint256' 668 | }, 669 | { 670 | internalType: 'uint256', 671 | name: 'state', 672 | type: 'uint256' 673 | } 674 | ], 675 | name: 'getStateInfoByIdAndState', 676 | outputs: [ 677 | { 678 | components: [ 679 | { 680 | internalType: 'uint256', 681 | name: 'id', 682 | type: 'uint256' 683 | }, 684 | { 685 | internalType: 'uint256', 686 | name: 'state', 687 | type: 'uint256' 688 | }, 689 | { 690 | internalType: 'uint256', 691 | name: 'replacedByState', 692 | type: 'uint256' 693 | }, 694 | { 695 | internalType: 'uint256', 696 | name: 'createdAtTimestamp', 697 | type: 'uint256' 698 | }, 699 | { 700 | internalType: 'uint256', 701 | name: 'replacedAtTimestamp', 702 | type: 'uint256' 703 | }, 704 | { 705 | internalType: 'uint256', 706 | name: 'createdAtBlock', 707 | type: 'uint256' 708 | }, 709 | { 710 | internalType: 'uint256', 711 | name: 'replacedAtBlock', 712 | type: 'uint256' 713 | } 714 | ], 715 | internalType: 'struct IState.StateInfo', 716 | name: '', 717 | type: 'tuple' 718 | } 719 | ], 720 | stateMutability: 'view', 721 | type: 'function' 722 | }, 723 | { 724 | inputs: [ 725 | { 726 | internalType: 'uint256', 727 | name: 'id', 728 | type: 'uint256' 729 | }, 730 | { 731 | internalType: 'uint256', 732 | name: 'startIndex', 733 | type: 'uint256' 734 | }, 735 | { 736 | internalType: 'uint256', 737 | name: 'length', 738 | type: 'uint256' 739 | } 740 | ], 741 | name: 'getStateInfoHistoryById', 742 | outputs: [ 743 | { 744 | components: [ 745 | { 746 | internalType: 'uint256', 747 | name: 'id', 748 | type: 'uint256' 749 | }, 750 | { 751 | internalType: 'uint256', 752 | name: 'state', 753 | type: 'uint256' 754 | }, 755 | { 756 | internalType: 'uint256', 757 | name: 'replacedByState', 758 | type: 'uint256' 759 | }, 760 | { 761 | internalType: 'uint256', 762 | name: 'createdAtTimestamp', 763 | type: 'uint256' 764 | }, 765 | { 766 | internalType: 'uint256', 767 | name: 'replacedAtTimestamp', 768 | type: 'uint256' 769 | }, 770 | { 771 | internalType: 'uint256', 772 | name: 'createdAtBlock', 773 | type: 'uint256' 774 | }, 775 | { 776 | internalType: 'uint256', 777 | name: 'replacedAtBlock', 778 | type: 'uint256' 779 | } 780 | ], 781 | internalType: 'struct IState.StateInfo[]', 782 | name: '', 783 | type: 'tuple[]' 784 | } 785 | ], 786 | stateMutability: 'view', 787 | type: 'function' 788 | }, 789 | { 790 | inputs: [ 791 | { 792 | internalType: 'uint256', 793 | name: 'id', 794 | type: 'uint256' 795 | } 796 | ], 797 | name: 'getStateInfoHistoryLengthById', 798 | outputs: [ 799 | { 800 | internalType: 'uint256', 801 | name: '', 802 | type: 'uint256' 803 | } 804 | ], 805 | stateMutability: 'view', 806 | type: 'function' 807 | }, 808 | { 809 | inputs: [], 810 | name: 'getVerifier', 811 | outputs: [ 812 | { 813 | internalType: 'address', 814 | name: '', 815 | type: 'address' 816 | } 817 | ], 818 | stateMutability: 'view', 819 | type: 'function' 820 | }, 821 | { 822 | inputs: [ 823 | { 824 | internalType: 'uint256', 825 | name: 'id', 826 | type: 'uint256' 827 | } 828 | ], 829 | name: 'idExists', 830 | outputs: [ 831 | { 832 | internalType: 'bool', 833 | name: '', 834 | type: 'bool' 835 | } 836 | ], 837 | stateMutability: 'view', 838 | type: 'function' 839 | }, 840 | { 841 | inputs: [ 842 | { 843 | internalType: 'contract IStateTransitionVerifier', 844 | name: 'verifierContractAddr', 845 | type: 'address' 846 | } 847 | ], 848 | name: 'initialize', 849 | outputs: [], 850 | stateMutability: 'nonpayable', 851 | type: 'function' 852 | }, 853 | { 854 | inputs: [], 855 | name: 'owner', 856 | outputs: [ 857 | { 858 | internalType: 'address', 859 | name: '', 860 | type: 'address' 861 | } 862 | ], 863 | stateMutability: 'view', 864 | type: 'function' 865 | }, 866 | { 867 | inputs: [], 868 | name: 'pendingOwner', 869 | outputs: [ 870 | { 871 | internalType: 'address', 872 | name: '', 873 | type: 'address' 874 | } 875 | ], 876 | stateMutability: 'view', 877 | type: 'function' 878 | }, 879 | { 880 | inputs: [], 881 | name: 'renounceOwnership', 882 | outputs: [], 883 | stateMutability: 'nonpayable', 884 | type: 'function' 885 | }, 886 | { 887 | inputs: [ 888 | { 889 | internalType: 'address', 890 | name: 'newVerifierAddr', 891 | type: 'address' 892 | } 893 | ], 894 | name: 'setVerifier', 895 | outputs: [], 896 | stateMutability: 'nonpayable', 897 | type: 'function' 898 | }, 899 | { 900 | inputs: [ 901 | { 902 | internalType: 'uint256', 903 | name: 'id', 904 | type: 'uint256' 905 | }, 906 | { 907 | internalType: 'uint256', 908 | name: 'state', 909 | type: 'uint256' 910 | } 911 | ], 912 | name: 'stateExists', 913 | outputs: [ 914 | { 915 | internalType: 'bool', 916 | name: '', 917 | type: 'bool' 918 | } 919 | ], 920 | stateMutability: 'view', 921 | type: 'function' 922 | }, 923 | { 924 | inputs: [ 925 | { 926 | internalType: 'address', 927 | name: 'newOwner', 928 | type: 'address' 929 | } 930 | ], 931 | name: 'transferOwnership', 932 | outputs: [], 933 | stateMutability: 'nonpayable', 934 | type: 'function' 935 | }, 936 | { 937 | inputs: [ 938 | { 939 | internalType: 'uint256', 940 | name: 'id', 941 | type: 'uint256' 942 | }, 943 | { 944 | internalType: 'uint256', 945 | name: 'oldState', 946 | type: 'uint256' 947 | }, 948 | { 949 | internalType: 'uint256', 950 | name: 'newState', 951 | type: 'uint256' 952 | }, 953 | { 954 | internalType: 'bool', 955 | name: 'isOldStateGenesis', 956 | type: 'bool' 957 | }, 958 | { 959 | internalType: 'uint256[2]', 960 | name: 'a', 961 | type: 'uint256[2]' 962 | }, 963 | { 964 | internalType: 'uint256[2][2]', 965 | name: 'b', 966 | type: 'uint256[2][2]' 967 | }, 968 | { 969 | internalType: 'uint256[2]', 970 | name: 'c', 971 | type: 'uint256[2]' 972 | } 973 | ], 974 | name: 'transitState', 975 | outputs: [], 976 | stateMutability: 'nonpayable', 977 | type: 'function' 978 | } 979 | ] as const; 980 | 981 | export class Abi__factory { 982 | static readonly abi = _abi; 983 | static createInterface(): AbiInterface { 984 | return new utils.Interface(_abi) as AbiInterface; 985 | } 986 | static connect(address: string, signerOrProvider: Signer | Provider): Abi { 987 | return new Contract(address, _abi, signerOrProvider) as Abi; 988 | } 989 | } 990 | -------------------------------------------------------------------------------- /abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": false, 7 | "internalType": "uint8", 8 | "name": "version", 9 | "type": "uint8" 10 | } 11 | ], 12 | "name": "Initialized", 13 | "type": "event" 14 | }, 15 | { 16 | "anonymous": false, 17 | "inputs": [ 18 | { 19 | "indexed": true, 20 | "internalType": "address", 21 | "name": "previousOwner", 22 | "type": "address" 23 | }, 24 | { 25 | "indexed": true, 26 | "internalType": "address", 27 | "name": "newOwner", 28 | "type": "address" 29 | } 30 | ], 31 | "name": "OwnershipTransferStarted", 32 | "type": "event" 33 | }, 34 | { 35 | "anonymous": false, 36 | "inputs": [ 37 | { 38 | "indexed": true, 39 | "internalType": "address", 40 | "name": "previousOwner", 41 | "type": "address" 42 | }, 43 | { 44 | "indexed": true, 45 | "internalType": "address", 46 | "name": "newOwner", 47 | "type": "address" 48 | } 49 | ], 50 | "name": "OwnershipTransferred", 51 | "type": "event" 52 | }, 53 | { 54 | "anonymous": false, 55 | "inputs": [ 56 | { 57 | "indexed": false, 58 | "internalType": "uint256", 59 | "name": "id", 60 | "type": "uint256" 61 | }, 62 | { 63 | "indexed": false, 64 | "internalType": "uint256", 65 | "name": "blockN", 66 | "type": "uint256" 67 | }, 68 | { 69 | "indexed": false, 70 | "internalType": "uint256", 71 | "name": "timestamp", 72 | "type": "uint256" 73 | }, 74 | { 75 | "indexed": false, 76 | "internalType": "uint256", 77 | "name": "state", 78 | "type": "uint256" 79 | } 80 | ], 81 | "name": "StateUpdated", 82 | "type": "event" 83 | }, 84 | { 85 | "inputs": [], 86 | "name": "VERSION", 87 | "outputs": [ 88 | { 89 | "internalType": "string", 90 | "name": "", 91 | "type": "string" 92 | } 93 | ], 94 | "stateMutability": "view", 95 | "type": "function" 96 | }, 97 | { 98 | "inputs": [], 99 | "name": "acceptOwnership", 100 | "outputs": [], 101 | "stateMutability": "nonpayable", 102 | "type": "function" 103 | }, 104 | { 105 | "inputs": [ 106 | { 107 | "internalType": "uint256", 108 | "name": "id", 109 | "type": "uint256" 110 | } 111 | ], 112 | "name": "getGISTProof", 113 | "outputs": [ 114 | { 115 | "components": [ 116 | { 117 | "internalType": "uint256", 118 | "name": "root", 119 | "type": "uint256" 120 | }, 121 | { 122 | "internalType": "bool", 123 | "name": "existence", 124 | "type": "bool" 125 | }, 126 | { 127 | "internalType": "uint256[64]", 128 | "name": "siblings", 129 | "type": "uint256[64]" 130 | }, 131 | { 132 | "internalType": "uint256", 133 | "name": "index", 134 | "type": "uint256" 135 | }, 136 | { 137 | "internalType": "uint256", 138 | "name": "value", 139 | "type": "uint256" 140 | }, 141 | { 142 | "internalType": "bool", 143 | "name": "auxExistence", 144 | "type": "bool" 145 | }, 146 | { 147 | "internalType": "uint256", 148 | "name": "auxIndex", 149 | "type": "uint256" 150 | }, 151 | { 152 | "internalType": "uint256", 153 | "name": "auxValue", 154 | "type": "uint256" 155 | } 156 | ], 157 | "internalType": "struct IState.GistProof", 158 | "name": "", 159 | "type": "tuple" 160 | } 161 | ], 162 | "stateMutability": "view", 163 | "type": "function" 164 | }, 165 | { 166 | "inputs": [ 167 | { 168 | "internalType": "uint256", 169 | "name": "id", 170 | "type": "uint256" 171 | }, 172 | { 173 | "internalType": "uint256", 174 | "name": "blockNumber", 175 | "type": "uint256" 176 | } 177 | ], 178 | "name": "getGISTProofByBlock", 179 | "outputs": [ 180 | { 181 | "components": [ 182 | { 183 | "internalType": "uint256", 184 | "name": "root", 185 | "type": "uint256" 186 | }, 187 | { 188 | "internalType": "bool", 189 | "name": "existence", 190 | "type": "bool" 191 | }, 192 | { 193 | "internalType": "uint256[64]", 194 | "name": "siblings", 195 | "type": "uint256[64]" 196 | }, 197 | { 198 | "internalType": "uint256", 199 | "name": "index", 200 | "type": "uint256" 201 | }, 202 | { 203 | "internalType": "uint256", 204 | "name": "value", 205 | "type": "uint256" 206 | }, 207 | { 208 | "internalType": "bool", 209 | "name": "auxExistence", 210 | "type": "bool" 211 | }, 212 | { 213 | "internalType": "uint256", 214 | "name": "auxIndex", 215 | "type": "uint256" 216 | }, 217 | { 218 | "internalType": "uint256", 219 | "name": "auxValue", 220 | "type": "uint256" 221 | } 222 | ], 223 | "internalType": "struct IState.GistProof", 224 | "name": "", 225 | "type": "tuple" 226 | } 227 | ], 228 | "stateMutability": "view", 229 | "type": "function" 230 | }, 231 | { 232 | "inputs": [ 233 | { 234 | "internalType": "uint256", 235 | "name": "id", 236 | "type": "uint256" 237 | }, 238 | { 239 | "internalType": "uint256", 240 | "name": "root", 241 | "type": "uint256" 242 | } 243 | ], 244 | "name": "getGISTProofByRoot", 245 | "outputs": [ 246 | { 247 | "components": [ 248 | { 249 | "internalType": "uint256", 250 | "name": "root", 251 | "type": "uint256" 252 | }, 253 | { 254 | "internalType": "bool", 255 | "name": "existence", 256 | "type": "bool" 257 | }, 258 | { 259 | "internalType": "uint256[64]", 260 | "name": "siblings", 261 | "type": "uint256[64]" 262 | }, 263 | { 264 | "internalType": "uint256", 265 | "name": "index", 266 | "type": "uint256" 267 | }, 268 | { 269 | "internalType": "uint256", 270 | "name": "value", 271 | "type": "uint256" 272 | }, 273 | { 274 | "internalType": "bool", 275 | "name": "auxExistence", 276 | "type": "bool" 277 | }, 278 | { 279 | "internalType": "uint256", 280 | "name": "auxIndex", 281 | "type": "uint256" 282 | }, 283 | { 284 | "internalType": "uint256", 285 | "name": "auxValue", 286 | "type": "uint256" 287 | } 288 | ], 289 | "internalType": "struct IState.GistProof", 290 | "name": "", 291 | "type": "tuple" 292 | } 293 | ], 294 | "stateMutability": "view", 295 | "type": "function" 296 | }, 297 | { 298 | "inputs": [ 299 | { 300 | "internalType": "uint256", 301 | "name": "id", 302 | "type": "uint256" 303 | }, 304 | { 305 | "internalType": "uint256", 306 | "name": "timestamp", 307 | "type": "uint256" 308 | } 309 | ], 310 | "name": "getGISTProofByTime", 311 | "outputs": [ 312 | { 313 | "components": [ 314 | { 315 | "internalType": "uint256", 316 | "name": "root", 317 | "type": "uint256" 318 | }, 319 | { 320 | "internalType": "bool", 321 | "name": "existence", 322 | "type": "bool" 323 | }, 324 | { 325 | "internalType": "uint256[64]", 326 | "name": "siblings", 327 | "type": "uint256[64]" 328 | }, 329 | { 330 | "internalType": "uint256", 331 | "name": "index", 332 | "type": "uint256" 333 | }, 334 | { 335 | "internalType": "uint256", 336 | "name": "value", 337 | "type": "uint256" 338 | }, 339 | { 340 | "internalType": "bool", 341 | "name": "auxExistence", 342 | "type": "bool" 343 | }, 344 | { 345 | "internalType": "uint256", 346 | "name": "auxIndex", 347 | "type": "uint256" 348 | }, 349 | { 350 | "internalType": "uint256", 351 | "name": "auxValue", 352 | "type": "uint256" 353 | } 354 | ], 355 | "internalType": "struct IState.GistProof", 356 | "name": "", 357 | "type": "tuple" 358 | } 359 | ], 360 | "stateMutability": "view", 361 | "type": "function" 362 | }, 363 | { 364 | "inputs": [], 365 | "name": "getGISTRoot", 366 | "outputs": [ 367 | { 368 | "internalType": "uint256", 369 | "name": "", 370 | "type": "uint256" 371 | } 372 | ], 373 | "stateMutability": "view", 374 | "type": "function" 375 | }, 376 | { 377 | "inputs": [ 378 | { 379 | "internalType": "uint256", 380 | "name": "start", 381 | "type": "uint256" 382 | }, 383 | { 384 | "internalType": "uint256", 385 | "name": "length", 386 | "type": "uint256" 387 | } 388 | ], 389 | "name": "getGISTRootHistory", 390 | "outputs": [ 391 | { 392 | "components": [ 393 | { 394 | "internalType": "uint256", 395 | "name": "root", 396 | "type": "uint256" 397 | }, 398 | { 399 | "internalType": "uint256", 400 | "name": "replacedByRoot", 401 | "type": "uint256" 402 | }, 403 | { 404 | "internalType": "uint256", 405 | "name": "createdAtTimestamp", 406 | "type": "uint256" 407 | }, 408 | { 409 | "internalType": "uint256", 410 | "name": "replacedAtTimestamp", 411 | "type": "uint256" 412 | }, 413 | { 414 | "internalType": "uint256", 415 | "name": "createdAtBlock", 416 | "type": "uint256" 417 | }, 418 | { 419 | "internalType": "uint256", 420 | "name": "replacedAtBlock", 421 | "type": "uint256" 422 | } 423 | ], 424 | "internalType": "struct IState.GistRootInfo[]", 425 | "name": "", 426 | "type": "tuple[]" 427 | } 428 | ], 429 | "stateMutability": "view", 430 | "type": "function" 431 | }, 432 | { 433 | "inputs": [], 434 | "name": "getGISTRootHistoryLength", 435 | "outputs": [ 436 | { 437 | "internalType": "uint256", 438 | "name": "", 439 | "type": "uint256" 440 | } 441 | ], 442 | "stateMutability": "view", 443 | "type": "function" 444 | }, 445 | { 446 | "inputs": [ 447 | { 448 | "internalType": "uint256", 449 | "name": "root", 450 | "type": "uint256" 451 | } 452 | ], 453 | "name": "getGISTRootInfo", 454 | "outputs": [ 455 | { 456 | "components": [ 457 | { 458 | "internalType": "uint256", 459 | "name": "root", 460 | "type": "uint256" 461 | }, 462 | { 463 | "internalType": "uint256", 464 | "name": "replacedByRoot", 465 | "type": "uint256" 466 | }, 467 | { 468 | "internalType": "uint256", 469 | "name": "createdAtTimestamp", 470 | "type": "uint256" 471 | }, 472 | { 473 | "internalType": "uint256", 474 | "name": "replacedAtTimestamp", 475 | "type": "uint256" 476 | }, 477 | { 478 | "internalType": "uint256", 479 | "name": "createdAtBlock", 480 | "type": "uint256" 481 | }, 482 | { 483 | "internalType": "uint256", 484 | "name": "replacedAtBlock", 485 | "type": "uint256" 486 | } 487 | ], 488 | "internalType": "struct IState.GistRootInfo", 489 | "name": "", 490 | "type": "tuple" 491 | } 492 | ], 493 | "stateMutability": "view", 494 | "type": "function" 495 | }, 496 | { 497 | "inputs": [ 498 | { 499 | "internalType": "uint256", 500 | "name": "blockNumber", 501 | "type": "uint256" 502 | } 503 | ], 504 | "name": "getGISTRootInfoByBlock", 505 | "outputs": [ 506 | { 507 | "components": [ 508 | { 509 | "internalType": "uint256", 510 | "name": "root", 511 | "type": "uint256" 512 | }, 513 | { 514 | "internalType": "uint256", 515 | "name": "replacedByRoot", 516 | "type": "uint256" 517 | }, 518 | { 519 | "internalType": "uint256", 520 | "name": "createdAtTimestamp", 521 | "type": "uint256" 522 | }, 523 | { 524 | "internalType": "uint256", 525 | "name": "replacedAtTimestamp", 526 | "type": "uint256" 527 | }, 528 | { 529 | "internalType": "uint256", 530 | "name": "createdAtBlock", 531 | "type": "uint256" 532 | }, 533 | { 534 | "internalType": "uint256", 535 | "name": "replacedAtBlock", 536 | "type": "uint256" 537 | } 538 | ], 539 | "internalType": "struct IState.GistRootInfo", 540 | "name": "", 541 | "type": "tuple" 542 | } 543 | ], 544 | "stateMutability": "view", 545 | "type": "function" 546 | }, 547 | { 548 | "inputs": [ 549 | { 550 | "internalType": "uint256", 551 | "name": "timestamp", 552 | "type": "uint256" 553 | } 554 | ], 555 | "name": "getGISTRootInfoByTime", 556 | "outputs": [ 557 | { 558 | "components": [ 559 | { 560 | "internalType": "uint256", 561 | "name": "root", 562 | "type": "uint256" 563 | }, 564 | { 565 | "internalType": "uint256", 566 | "name": "replacedByRoot", 567 | "type": "uint256" 568 | }, 569 | { 570 | "internalType": "uint256", 571 | "name": "createdAtTimestamp", 572 | "type": "uint256" 573 | }, 574 | { 575 | "internalType": "uint256", 576 | "name": "replacedAtTimestamp", 577 | "type": "uint256" 578 | }, 579 | { 580 | "internalType": "uint256", 581 | "name": "createdAtBlock", 582 | "type": "uint256" 583 | }, 584 | { 585 | "internalType": "uint256", 586 | "name": "replacedAtBlock", 587 | "type": "uint256" 588 | } 589 | ], 590 | "internalType": "struct IState.GistRootInfo", 591 | "name": "", 592 | "type": "tuple" 593 | } 594 | ], 595 | "stateMutability": "view", 596 | "type": "function" 597 | }, 598 | { 599 | "inputs": [ 600 | { 601 | "internalType": "uint256", 602 | "name": "id", 603 | "type": "uint256" 604 | } 605 | ], 606 | "name": "getStateInfoById", 607 | "outputs": [ 608 | { 609 | "components": [ 610 | { 611 | "internalType": "uint256", 612 | "name": "id", 613 | "type": "uint256" 614 | }, 615 | { 616 | "internalType": "uint256", 617 | "name": "state", 618 | "type": "uint256" 619 | }, 620 | { 621 | "internalType": "uint256", 622 | "name": "replacedByState", 623 | "type": "uint256" 624 | }, 625 | { 626 | "internalType": "uint256", 627 | "name": "createdAtTimestamp", 628 | "type": "uint256" 629 | }, 630 | { 631 | "internalType": "uint256", 632 | "name": "replacedAtTimestamp", 633 | "type": "uint256" 634 | }, 635 | { 636 | "internalType": "uint256", 637 | "name": "createdAtBlock", 638 | "type": "uint256" 639 | }, 640 | { 641 | "internalType": "uint256", 642 | "name": "replacedAtBlock", 643 | "type": "uint256" 644 | } 645 | ], 646 | "internalType": "struct IState.StateInfo", 647 | "name": "", 648 | "type": "tuple" 649 | } 650 | ], 651 | "stateMutability": "view", 652 | "type": "function" 653 | }, 654 | { 655 | "inputs": [ 656 | { 657 | "internalType": "uint256", 658 | "name": "id", 659 | "type": "uint256" 660 | }, 661 | { 662 | "internalType": "uint256", 663 | "name": "state", 664 | "type": "uint256" 665 | } 666 | ], 667 | "name": "getStateInfoByIdAndState", 668 | "outputs": [ 669 | { 670 | "components": [ 671 | { 672 | "internalType": "uint256", 673 | "name": "id", 674 | "type": "uint256" 675 | }, 676 | { 677 | "internalType": "uint256", 678 | "name": "state", 679 | "type": "uint256" 680 | }, 681 | { 682 | "internalType": "uint256", 683 | "name": "replacedByState", 684 | "type": "uint256" 685 | }, 686 | { 687 | "internalType": "uint256", 688 | "name": "createdAtTimestamp", 689 | "type": "uint256" 690 | }, 691 | { 692 | "internalType": "uint256", 693 | "name": "replacedAtTimestamp", 694 | "type": "uint256" 695 | }, 696 | { 697 | "internalType": "uint256", 698 | "name": "createdAtBlock", 699 | "type": "uint256" 700 | }, 701 | { 702 | "internalType": "uint256", 703 | "name": "replacedAtBlock", 704 | "type": "uint256" 705 | } 706 | ], 707 | "internalType": "struct IState.StateInfo", 708 | "name": "", 709 | "type": "tuple" 710 | } 711 | ], 712 | "stateMutability": "view", 713 | "type": "function" 714 | }, 715 | { 716 | "inputs": [ 717 | { 718 | "internalType": "uint256", 719 | "name": "id", 720 | "type": "uint256" 721 | }, 722 | { 723 | "internalType": "uint256", 724 | "name": "startIndex", 725 | "type": "uint256" 726 | }, 727 | { 728 | "internalType": "uint256", 729 | "name": "length", 730 | "type": "uint256" 731 | } 732 | ], 733 | "name": "getStateInfoHistoryById", 734 | "outputs": [ 735 | { 736 | "components": [ 737 | { 738 | "internalType": "uint256", 739 | "name": "id", 740 | "type": "uint256" 741 | }, 742 | { 743 | "internalType": "uint256", 744 | "name": "state", 745 | "type": "uint256" 746 | }, 747 | { 748 | "internalType": "uint256", 749 | "name": "replacedByState", 750 | "type": "uint256" 751 | }, 752 | { 753 | "internalType": "uint256", 754 | "name": "createdAtTimestamp", 755 | "type": "uint256" 756 | }, 757 | { 758 | "internalType": "uint256", 759 | "name": "replacedAtTimestamp", 760 | "type": "uint256" 761 | }, 762 | { 763 | "internalType": "uint256", 764 | "name": "createdAtBlock", 765 | "type": "uint256" 766 | }, 767 | { 768 | "internalType": "uint256", 769 | "name": "replacedAtBlock", 770 | "type": "uint256" 771 | } 772 | ], 773 | "internalType": "struct IState.StateInfo[]", 774 | "name": "", 775 | "type": "tuple[]" 776 | } 777 | ], 778 | "stateMutability": "view", 779 | "type": "function" 780 | }, 781 | { 782 | "inputs": [ 783 | { 784 | "internalType": "uint256", 785 | "name": "id", 786 | "type": "uint256" 787 | } 788 | ], 789 | "name": "getStateInfoHistoryLengthById", 790 | "outputs": [ 791 | { 792 | "internalType": "uint256", 793 | "name": "", 794 | "type": "uint256" 795 | } 796 | ], 797 | "stateMutability": "view", 798 | "type": "function" 799 | }, 800 | { 801 | "inputs": [], 802 | "name": "getVerifier", 803 | "outputs": [ 804 | { 805 | "internalType": "address", 806 | "name": "", 807 | "type": "address" 808 | } 809 | ], 810 | "stateMutability": "view", 811 | "type": "function" 812 | }, 813 | { 814 | "inputs": [ 815 | { 816 | "internalType": "uint256", 817 | "name": "id", 818 | "type": "uint256" 819 | } 820 | ], 821 | "name": "idExists", 822 | "outputs": [ 823 | { 824 | "internalType": "bool", 825 | "name": "", 826 | "type": "bool" 827 | } 828 | ], 829 | "stateMutability": "view", 830 | "type": "function" 831 | }, 832 | { 833 | "inputs": [ 834 | { 835 | "internalType": "contract IStateTransitionVerifier", 836 | "name": "verifierContractAddr", 837 | "type": "address" 838 | } 839 | ], 840 | "name": "initialize", 841 | "outputs": [], 842 | "stateMutability": "nonpayable", 843 | "type": "function" 844 | }, 845 | { 846 | "inputs": [], 847 | "name": "owner", 848 | "outputs": [ 849 | { 850 | "internalType": "address", 851 | "name": "", 852 | "type": "address" 853 | } 854 | ], 855 | "stateMutability": "view", 856 | "type": "function" 857 | }, 858 | { 859 | "inputs": [], 860 | "name": "pendingOwner", 861 | "outputs": [ 862 | { 863 | "internalType": "address", 864 | "name": "", 865 | "type": "address" 866 | } 867 | ], 868 | "stateMutability": "view", 869 | "type": "function" 870 | }, 871 | { 872 | "inputs": [], 873 | "name": "renounceOwnership", 874 | "outputs": [], 875 | "stateMutability": "nonpayable", 876 | "type": "function" 877 | }, 878 | { 879 | "inputs": [ 880 | { 881 | "internalType": "address", 882 | "name": "newVerifierAddr", 883 | "type": "address" 884 | } 885 | ], 886 | "name": "setVerifier", 887 | "outputs": [], 888 | "stateMutability": "nonpayable", 889 | "type": "function" 890 | }, 891 | { 892 | "inputs": [ 893 | { 894 | "internalType": "uint256", 895 | "name": "id", 896 | "type": "uint256" 897 | }, 898 | { 899 | "internalType": "uint256", 900 | "name": "state", 901 | "type": "uint256" 902 | } 903 | ], 904 | "name": "stateExists", 905 | "outputs": [ 906 | { 907 | "internalType": "bool", 908 | "name": "", 909 | "type": "bool" 910 | } 911 | ], 912 | "stateMutability": "view", 913 | "type": "function" 914 | }, 915 | { 916 | "inputs": [ 917 | { 918 | "internalType": "address", 919 | "name": "newOwner", 920 | "type": "address" 921 | } 922 | ], 923 | "name": "transferOwnership", 924 | "outputs": [], 925 | "stateMutability": "nonpayable", 926 | "type": "function" 927 | }, 928 | { 929 | "inputs": [ 930 | { 931 | "internalType": "uint256", 932 | "name": "id", 933 | "type": "uint256" 934 | }, 935 | { 936 | "internalType": "uint256", 937 | "name": "oldState", 938 | "type": "uint256" 939 | }, 940 | { 941 | "internalType": "uint256", 942 | "name": "newState", 943 | "type": "uint256" 944 | }, 945 | { 946 | "internalType": "bool", 947 | "name": "isOldStateGenesis", 948 | "type": "bool" 949 | }, 950 | { 951 | "internalType": "uint256[2]", 952 | "name": "a", 953 | "type": "uint256[2]" 954 | }, 955 | { 956 | "internalType": "uint256[2][2]", 957 | "name": "b", 958 | "type": "uint256[2][2]" 959 | }, 960 | { 961 | "internalType": "uint256[2]", 962 | "name": "c", 963 | "type": "uint256[2]" 964 | } 965 | ], 966 | "name": "transitState", 967 | "outputs": [], 968 | "stateMutability": "nonpayable", 969 | "type": "function" 970 | } 971 | ] 972 | -------------------------------------------------------------------------------- /test/atomicV3.test.ts: -------------------------------------------------------------------------------- 1 | import { Verifier } from '@lib/auth/auth'; 2 | import { testOpts, resolvers, getTestDataPath } from './mocks'; 3 | import { 4 | AuthorizationResponseMessage, 5 | PROTOCOL_CONSTANTS, 6 | AuthorizationRequestMessage, 7 | cacheLoader, 8 | CircuitId, 9 | ProofType 10 | } from '@0xpolygonid/js-sdk'; 11 | import { DocumentLoader } from '@iden3/js-jsonld-merklization'; 12 | 13 | const schemaLoader: DocumentLoader = cacheLoader({ 14 | ipfsNodeURL: process.env.IPFS_URL ?? 'https://ipfs.io' 15 | }); 16 | describe('atomicV3', () => { 17 | it('TestVerifyV3MessageWithSigProof_NonMerklized', async () => { 18 | const request: AuthorizationRequestMessage = { 19 | id: '28b15cd4-3aa1-4ddc-88a3-c05a0f788065', 20 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 21 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, 22 | thid: '28b15cd4-3aa1-4ddc-88a3-c05a0f788065', 23 | body: { 24 | callbackUrl: 'https://test.com/callback', 25 | reason: 'test', 26 | message: 'message to sign', 27 | scope: [ 28 | { 29 | id: 84239, 30 | circuitId: CircuitId.AtomicQueryV3, 31 | optional: true, 32 | query: { 33 | allowedIssuers: [ 34 | 'did:polygonid:polygon:mumbai:2qHwoMVgF22ozYfs4gXiC8rr6S3sBCr2WSQwkRTfB3' 35 | ], 36 | context: 37 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-nonmerklized.jsonld', 38 | credentialSubject: { documentType: { $eq: 99 } }, 39 | proofType: ProofType.BJJSignature, 40 | type: 'KYCAgeCredential' 41 | } 42 | } 43 | ] 44 | }, 45 | from: 'did:polygonid:polygon:mumbai:2qHwoMVgF22ozYfs4gXiC8rr6S3sBCr2WSQwkRTfB3' 46 | }; 47 | 48 | // response 49 | const message: AuthorizationResponseMessage = { 50 | id: '59fbefd2-39ce-4346-94f1-49ec86141ba9', 51 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 52 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_RESPONSE_MESSAGE_TYPE, 53 | thid: '28b15cd4-3aa1-4ddc-88a3-c05a0f788065', 54 | body: { 55 | message: 'message to sign', 56 | scope: [ 57 | { 58 | id: 84239, 59 | circuitId: CircuitId.AtomicQueryV3, 60 | proof: { 61 | pi_a: [ 62 | '16259159015885704203972860572159615143955018856040187471443250070675076694830', 63 | '9936261134972021495913066861635092844201384322741097016561225846815554727861', 64 | '1' 65 | ], 66 | pi_b: [ 67 | [ 68 | '13274307415608622554787983733075359594805362696573187221363487029527021649751', 69 | '6770083709194565352752538013885988394082029538687547975296275830191977093579' 70 | ], 71 | [ 72 | '9858564313568500515580682604962916226991978376542020052463904057033098942989', 73 | '13481074478476721746530420758311031367861669381777251718356676850384797753756' 74 | ], 75 | ['1', '0'] 76 | ], 77 | pi_c: [ 78 | '8149221637512194456411857416264300795155888057920064325736883074311578592724', 79 | '16109585571689383482058996181597945116005469364555566560159598173964374245998', 80 | '1' 81 | ], 82 | protocol: 'groth16' 83 | }, 84 | pub_signals: [ 85 | '0', 86 | '21575127216236248869702276246037557119007466180301957762196593786733007362', 87 | '4487386332479489158003597844990487984925471813907462483907054425759564175341', 88 | '0', 89 | '0', 90 | '0', 91 | '1', 92 | '84239', 93 | '25198543381200665770805816046271594885604002445105767653616878167826895362', 94 | '1', 95 | '4487386332479489158003597844990487984925471813907462483907054425759564175341', 96 | '1710949149', 97 | '198285726510688200335207273836123338699', 98 | '0', 99 | '3', 100 | '1', 101 | '99', 102 | '0', 103 | '0', 104 | '0', 105 | '0', 106 | '0', 107 | '0', 108 | '0', 109 | '0', 110 | '0', 111 | '0', 112 | '0', 113 | '0', 114 | '0', 115 | '0', 116 | '0', 117 | '0', 118 | '0', 119 | '0', 120 | '0', 121 | '0', 122 | '0', 123 | '0', 124 | '0', 125 | '0', 126 | '0', 127 | '0', 128 | '0', 129 | '0', 130 | '0', 131 | '0', 132 | '0', 133 | '0', 134 | '0', 135 | '0', 136 | '0', 137 | '0', 138 | '0', 139 | '0', 140 | '0', 141 | '0', 142 | '0', 143 | '0', 144 | '0', 145 | '0', 146 | '0', 147 | '0', 148 | '0', 149 | '0', 150 | '0', 151 | '0', 152 | '0', 153 | '0', 154 | '0', 155 | '0', 156 | '0', 157 | '0', 158 | '0', 159 | '0', 160 | '0', 161 | '0', 162 | '0', 163 | '0', 164 | '0', 165 | '1', 166 | '25198543381200665770805816046271594885604002445105767653616878167826895362', 167 | '0' 168 | ] 169 | } 170 | ] 171 | }, 172 | from: 'did:polygonid:polygon:mumbai:2qD58KvD3mPB1H1dZKhDPRhEd3aE1Fdx3iGd5VjcHq', 173 | to: 'did:polygonid:polygon:mumbai:2qHwoMVgF22ozYfs4gXiC8rr6S3sBCr2WSQwkRTfB3' 174 | }; 175 | 176 | const authInstance = await Verifier.newVerifier({ 177 | stateResolver: resolvers, 178 | circuitsDir: getTestDataPath('./testdata') 179 | }); 180 | 181 | await authInstance.verifyAuthResponse(message, request, testOpts); 182 | }); 183 | 184 | it('TestVerifyV3MessageWithMtpProof_Merklized', async () => { 185 | const request: AuthorizationRequestMessage = { 186 | id: '7e5b5847-b479-4499-90ee-5fe4826a5bdd', 187 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 188 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, 189 | thid: '7e5b5847-b479-4499-90ee-5fe4826a5bdd', 190 | body: { 191 | callbackUrl: 'https://test.com/callback', 192 | reason: 'test', 193 | message: 'message to sign', 194 | scope: [ 195 | { 196 | id: 84239, 197 | circuitId: CircuitId.AtomicQueryV3, 198 | optional: true, 199 | query: { 200 | allowedIssuers: [ 201 | 'did:polygonid:polygon:mumbai:2qHwoMVgF22ozYfs4gXiC8rr6S3sBCr2WSQwkRTfB3' 202 | ], 203 | context: 204 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v101.json-ld', 205 | credentialSubject: { ZKPexperiance: { $eq: true } }, 206 | proofType: ProofType.Iden3SparseMerkleTreeProof, 207 | type: 'KYCEmployee' 208 | } 209 | } 210 | ] 211 | }, 212 | from: 'did:polygonid:polygon:mumbai:2qHwoMVgF22ozYfs4gXiC8rr6S3sBCr2WSQwkRTfB3' 213 | }; 214 | 215 | const message: AuthorizationResponseMessage = { 216 | id: 'ac381820-21af-499a-8c5d-8f01fca9783c', 217 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 218 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_RESPONSE_MESSAGE_TYPE, 219 | thid: '7e5b5847-b479-4499-90ee-5fe4826a5bdd', 220 | body: { 221 | message: 'message to sign', 222 | scope: [ 223 | { 224 | id: 84239, 225 | circuitId: CircuitId.AtomicQueryV3, 226 | proof: { 227 | pi_a: [ 228 | '3861193683666781975306242203140068346756283860682260416444665826971771307548', 229 | '21810894358036153056319810051635175931407878645189526517430393009315784695000', 230 | '1' 231 | ], 232 | pi_b: [ 233 | [ 234 | '18298965671920870484411439834774874943637171740999661387337056943427101377004', 235 | '3258146086436440871190787989125169755873616628531313710472823132022824092498' 236 | ], 237 | [ 238 | '8827991569608995396514322600532414912786284032230818679651315512397940335503', 239 | '16062431852624907726401854149167559679027469110872523439460150345436579952148' 240 | ], 241 | ['1', '0'] 242 | ], 243 | pi_c: [ 244 | '10876640267586617362267882068785812826867213950790000254336613394780553351750', 245 | '17775411727021189595749368764576250038182277003123950451468780261229798413075', 246 | '1' 247 | ], 248 | protocol: 'groth16' 249 | }, 250 | pub_signals: [ 251 | '1', 252 | '21575127216236248869702276246037557119007466180301957762196593786733007362', 253 | '10316494485353306028292038000082940935171221819379372920844877797885116437287', 254 | '0', 255 | '0', 256 | '0', 257 | '2', 258 | '84239', 259 | '25198543381200665770805816046271594885604002445105767653616878167826895362', 260 | '1', 261 | '4487386332479489158003597844990487984925471813907462483907054425759564175341', 262 | '1710948584', 263 | '219578617064540016234161640375755865412', 264 | '1944808975288007371356450257872165609440470546066507760733183342797918372827', 265 | '0', 266 | '1', 267 | '18586133768512220936620570745912940619677854269274689475585506675881198879027', 268 | '0', 269 | '0', 270 | '0', 271 | '0', 272 | '0', 273 | '0', 274 | '0', 275 | '0', 276 | '0', 277 | '0', 278 | '0', 279 | '0', 280 | '0', 281 | '0', 282 | '0', 283 | '0', 284 | '0', 285 | '0', 286 | '0', 287 | '0', 288 | '0', 289 | '0', 290 | '0', 291 | '0', 292 | '0', 293 | '0', 294 | '0', 295 | '0', 296 | '0', 297 | '0', 298 | '0', 299 | '0', 300 | '0', 301 | '0', 302 | '0', 303 | '0', 304 | '0', 305 | '0', 306 | '0', 307 | '0', 308 | '0', 309 | '0', 310 | '0', 311 | '0', 312 | '0', 313 | '0', 314 | '0', 315 | '0', 316 | '0', 317 | '0', 318 | '0', 319 | '0', 320 | '0', 321 | '0', 322 | '0', 323 | '0', 324 | '0', 325 | '0', 326 | '0', 327 | '0', 328 | '0', 329 | '0', 330 | '0', 331 | '1', 332 | '25198543381200665770805816046271594885604002445105767653616878167826895362', 333 | '0' 334 | ] 335 | } 336 | ] 337 | }, 338 | from: 'did:polygonid:polygon:mumbai:2qD58KvD3mPB1H1dZKhDPRhEd3aE1Fdx3iGd5VjcHq', 339 | to: 'did:polygonid:polygon:mumbai:2qHwoMVgF22ozYfs4gXiC8rr6S3sBCr2WSQwkRTfB3' 340 | }; 341 | 342 | const authInstance = await Verifier.newVerifier({ 343 | stateResolver: resolvers, 344 | circuitsDir: getTestDataPath('./testdata') 345 | }); 346 | 347 | await authInstance.verifyAuthResponse(message, request, testOpts); 348 | }); 349 | 350 | it('auth with atomicV3 (nullifier, 2 req (merklized and non-merklized))', async () => { 351 | const request: AuthorizationRequestMessage = { 352 | id: '7d22275a-b518-45bb-8ee1-85e12abd8532', 353 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 354 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, 355 | thid: '7d22275a-b518-45bb-8ee1-85e12abd8532', 356 | body: { 357 | callbackUrl: 'http://localhost:8080/callback?id=1234442-123123-123123', 358 | reason: 'reason', 359 | message: 'message', 360 | scope: [ 361 | { 362 | id: 1, 363 | circuitId: CircuitId.AtomicQueryV3, 364 | optional: false, 365 | query: { 366 | groupId: 2, 367 | allowedIssuers: ['*'], 368 | type: 'KYCAgeCredential', 369 | proofType: ProofType.BJJSignature, 370 | context: 371 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-nonmerklized.jsonld', 372 | credentialSubject: { 373 | documentType: { 374 | $eq: 99 375 | } 376 | } 377 | } 378 | }, 379 | { 380 | id: 2, 381 | circuitId: CircuitId.AtomicQueryV3, 382 | optional: false, 383 | params: { 384 | nullifierSessionId: 12345 385 | }, 386 | query: { 387 | groupId: 1, 388 | proofType: ProofType.Iden3SparseMerkleTreeProof, 389 | allowedIssuers: ['*'], 390 | type: 'KYCEmployee', 391 | context: 392 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v101.json-ld', 393 | credentialSubject: { 394 | hireDate: { 395 | $eq: '2023-12-11' 396 | } 397 | } 398 | } 399 | } 400 | ] 401 | }, 402 | from: 'did:iden3:polygon:amoy:xCRp75DgAdS63W65fmXHz6p9DwdonuRU9e46DifhX' 403 | }; 404 | 405 | const verifier = await Verifier.newVerifier({ 406 | stateResolver: resolvers, 407 | circuitsDir: getTestDataPath('./testdata'), 408 | documentLoader: schemaLoader 409 | }); 410 | 411 | const token = 412 | 'eyJhbGciOiJncm90aDE2IiwiY2lyY3VpdElkIjoiYXV0aFYyIiwiY3JpdCI6WyJjaXJjdWl0SWQiXSwidHlwIjoiYXBwbGljYXRpb24vaWRlbjMtemtwLWpzb24ifQ.eyJpZCI6IjMzYmU1YjE4LWIyYzktNDdmZi1hMTJlLTA0OThhNjdlNDYyYyIsInR5cCI6ImFwcGxpY2F0aW9uL2lkZW4zLXprcC1qc29uIiwidHlwZSI6Imh0dHBzOi8vaWRlbjMtY29tbXVuaWNhdGlvbi5pby9hdXRob3JpemF0aW9uLzEuMC9yZXNwb25zZSIsInRoaWQiOiJhYzAxZWYyYy04MWNiLTRlMTUtYjYxYS01N2QyMGQ4Mjg4YzIiLCJib2R5Ijp7Im1lc3NhZ2UiOiJtZXNzYWdlIiwic2NvcGUiOlt7ImlkIjoxLCJjaXJjdWl0SWQiOiJjcmVkZW50aWFsQXRvbWljUXVlcnlWMy1iZXRhLjEiLCJwcm9vZiI6eyJwaV9hIjpbIjE3NTE0Nzg3Mjc4OTIyMzg4MTIyOTE2MDg1OTk5MjI0NzUxNTMyNTQwNTQ2OTcxNjQ2Nzc5NTQwNTEyOTI1MDQ0MDEzMDAzMzM5NDkwIiwiMjk2NjE1MTY0NjMzODM5Njc4Mzk1NjgyMjM0NjQzOTg0NTU3NjM3OTAxMzEzMzk0MzY3MzczNTY4MDAwOTM2MjQxODcwODg5NjQxMSIsIjEiXSwicGlfYiI6W1siMzY1MDgwMzg5NjQwNDIzMzA4NjczOTM0NjI1MjYzODU5NTMwODcyODI4NzA2OTkzNDAyOTQzMDUzODUyMTE4NzMyOTgwMjEzNDk3IiwiMTg5MTc5ODU3ODMyNzg3NzgyMjQyNDY3NTc5Nzg3NjQ3NzAwMTM4OTM5NjcyNjIyNzc5MTk1Mzc0MDA0MzU3MjAyMDA3MDEyMTMyODgiXSxbIjcyOTM4NzI2MDYxOTg1MTgwODQ4NTk1MDk3NTExNDkyMTcyNTYyMDI4ODk2MjMzMDc0MzgzODY3OTM3MDMwNTQyODE1NDAyNDkwNjIiLCI3ODcyNjYxMTIzMzA1NTczMzA5MjUzNzgxNTgzMjc1MDk2MTg2NDc0Mzg5MzU3NTcyMzE2MDIxMDQ1ODAwODM4Mjk5NTE5MzAzODMzIl0sWyIxIiwiMCJdXSwicGlfYyI6WyIyMDc3MDU1OTk5NTYwMzQ4NzM4NjYyMTE3NzE2MTM2MzI5Njc3NjM1MDg1MTA5MzYyOTM1OTQ5OTQxMjUyMzQyNTIxODg3MzYxOTg3NyIsIjE4MzU1NDY5OTQ3MTExNTUyNjUzNjY5ODI5NDU4MzE0NTMzNTI4OTM2MDEzMDQzNzc2OTAzMzIyOTI4NTE2NzY0MTUyODk5Nzc5NTIyIiwiMSJdLCJwcm90b2NvbCI6Imdyb3RoMTYiLCJjdXJ2ZSI6ImJuMTI4In0sInB1Yl9zaWduYWxzIjpbIjAiLCIyMTU3NTEyNzIxNjIzNjI0ODg2OTcwMjI3NjI0NjAzNzU1NzExOTAwNzQ2NjE4MDMwMTk1Nzc2MjE5NjU5Mzc4NjczMzAwNzYxNyIsIjQ0ODczODYzMzI0Nzk0ODkxNTgwMDM1OTc4NDQ5OTA0ODc5ODQ5MjU0NzE4MTM5MDc0NjI0ODM5MDcwNTQ0MjU3NTk1NjQxNzUzNDEiLCIxNTIxMjMyMDY1MTQyMDg0MzcyMjQzMDg1NzU5MzI1NDY5MzA5NTU2OTI2NzY5Mjg5MzM1OTM1Mjc1NDA0NjExNjgyNjM2MDQyNzA3MiIsIjAiLCIwIiwiMSIsIjEiLCIyNTE5ODU0MzM4MTIwMDY2NTc3MDgwNTgxNjA0NjI3MTU5NDg4NTYwNDAwMjQ0NTEwNTc2NzY1MzYxNjg3ODE2NzgyNjg5NTYxNyIsIjEiLCI0NDg3Mzg2MzMyNDc5NDg5MTU4MDAzNTk3ODQ0OTkwNDg3OTg0OTI1NDcxODEzOTA3NDYyNDgzOTA3MDU0NDI1NzU5NTY0MTc1MzQxIiwiMTcyNTkwOTEyNSIsIjE5ODI4NTcyNjUxMDY4ODIwMDMzNTIwNzI3MzgzNjEyMzMzODY5OSIsIjAiLCIzIiwiMSIsIjk5IiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMSIsIjI1MTk4NTQzMzgxMjAwNjY1NzcwODA1ODE2MDQ2MjcxNTk0ODg1NjA0MDAyNDQ1MTA1NzY3NjUzNjE2ODc4MTY3ODI2ODk1NjE3IiwiMCJdfSx7ImlkIjoyLCJjaXJjdWl0SWQiOiJjcmVkZW50aWFsQXRvbWljUXVlcnlWMy1iZXRhLjEiLCJwcm9vZiI6eyJwaV9hIjpbIjIwMjM3Nzg4NTQ0MTgzMzIyODk2MjU1MTU4ODQ0NjM4MDI2NDU2NzM5MDEwMzAyNzE2MTMyNjExODgwNDU4NTU1NzY5MjY1NjYxNTYyIiwiMTU1NTYxOTY4NjIyOTk1MDIyOTYyOTE4MDAzNzA5MjE5MjM0NjEzMjAzMDQxMTkxMjkyMTU0NzE2NTM5NDYwMTUxNzg1MjE0ODYzNDEiLCIxIl0sInBpX2IiOltbIjE2NTIwNTExMTM2MjQwODM0MzE4MTg2MDg3MjM4NTQxNDA4NTc2OTE5MTUyNjI1ODExNzY3MTUyMzA4MTI5MzU3MzM5NDQ4MjQ4MTc4IiwiMjU4MTAzNTc2NjU0MTU0NjkwMzg5MzQ1MzI2MDA3MjQxMjI1MzkxNTgxNjEwODY0NDM5MzI4MDkxNzczODQwMTQ2NjUwMTM1NDQ3NyJdLFsiOTM2ODAxODIzNzI1NTc0OTA0OTA0OTUzNzUzNjMwNjg2MDg2NDYzNDI1NTI1OTU0NzQ3NDY1OTUxMjE0NTkwOTU5MDk4ODYwNTkwNSIsIjExNjcxOTMyMTU4MTgxODk2MzgyNjA2NDY0Mzk1NjM3OTMxNjc1MjIzMDE2MDc4NDE5MzY1MzM4MjczODcxNDcwNDA5NDQwNDU2Mzc2Il0sWyIxIiwiMCJdXSwicGlfYyI6WyIxODQzODUxMTkzMzEyMTg0ODcyODY5NjU2MjI3NDM3NTc0NTQ0NDU5OTcwOTQ5NDgyNzM5MzY0Mzg3OTkzMjUxODQwOTU5NjU4OTkxNiIsIjE2NDcwMTgwMzE0MDI3NjI2MjUwMjQzMjU3NDYzNjcwMjU3MjQ4NjY0Nzg5MjM1OTMzMTg3NzkzOTg1MDU4MjI4NjQ2MzE0ODUxMzMyIiwiMSJdLCJwcm90b2NvbCI6Imdyb3RoMTYiLCJjdXJ2ZSI6ImJuMTI4In0sInB1Yl9zaWduYWxzIjpbIjEiLCIyMTU3NTEyNzIxNjIzNjI0ODg2OTcwMjI3NjI0NjAzNzU1NzExOTAwNzQ2NjE4MDMwMTk1Nzc2MjE5NjU5Mzc4NjczMzAwNzYxNyIsIjMxMTIyOTA2NDMxNTQxMTY4OTg5NzI4MzE2OTg0MzU2MTEwNjYwNTUzOTY0Mjg1MTY1MDIwOTk5ODQ2NTk4MzcwOTAyMjc1NjIzNCIsIjIwMDQ1MzY5OTA0MzIzMzE3ODc1MTYxMjM2NDkzOTE3NzU3ODUyNDc4MDQ0MDQxMjk2NjExNDEwNDc3MTk2MjIzODEwMTE1Nzg0ODAyIiwiNTExMzExMDc0MjE2MzU2MTE2MTY4MTExMDYwMDg1ODAxODg4NjQ0MDI5MTI4ODk2MjY4MTIzMzQyOTk3NTEzMzkxNjM0ODYwNjUyMSIsIjAiLCIyIiwiMiIsIjI1MTk4NTQzMzgxMjAwNjY1NzcwODA1ODE2MDQ2MjcxNTk0ODg1NjA0MDAyNDQ1MTA1NzY3NjUzNjE2ODc4MTY3ODI2ODk1NjE3IiwiMSIsIjQ0ODczODYzMzI0Nzk0ODkxNTgwMDM1OTc4NDQ5OTA0ODc5ODQ5MjU0NzE4MTM5MDc0NjI0ODM5MDcwNTQ0MjU3NTk1NjQxNzUzNDEiLCIxNzI1OTA5MTMwIiwiMjE5NTc4NjE3MDY0NTQwMDE2MjM0MTYxNjQwMzc1NzU1ODY1NDEyIiwiMTI5NjM1MTc1ODI2OTA2MTE3MzMxNzEwNTA0MTk2ODA2NzA3NzQ1MTkxNDM4NjA4NjIyMjkzMTUxNjE5OTE5NDk1OTg2OTQ2Mzg4MiIsIjAiLCIxIiwiMTcwMjI1MjgwMDAwMDAwMDAwMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjAiLCIwIiwiMCIsIjEiLCIyNTE5ODU0MzM4MTIwMDY2NTc3MDgwNTgxNjA0NjI3MTU5NDg4NTYwNDAwMjQ0NTEwNTc2NzY1MzYxNjg3ODE2NzgyNjg5NTYxNyIsIjEyMzQ1Il19XX0sImZyb20iOiJkaWQ6aWRlbjM6cG9seWdvbjphbW95Ong3Wjk1VmtVdXlvNm1xcmFKdzJWR3dDZnFUemRxaE0xUlZqUkh6Y3BLIiwidG8iOiJkaWQ6aWRlbjM6cG9seWdvbjphbW95OnhDUnA3NURnQWRTNjNXNjVmbVhIejZwOUR3ZG9udVJVOWU0NkRpZmhYIn0.eyJwcm9vZiI6eyJwaV9hIjpbIjExMTk4MDQ3ODYxNzgxNjYyNTc2NzM4NzA3NDM2NjE3NjY0MTI0MDI2NDA5MzA3Mjk2MjAxNjA3Nzg2MTYzNjM4NzgyMTc2MTkwNTYxIiwiMzM5Njc3MTI2Njg0NjMwNDA5NjIzMjU4OTgxODMxNTc3OTYxMTg1NjY5MzU0NDI5ODU3OTM1NTM1ODQ1NzEyMjA1OTExNjc0MDQ2OSIsIjEiXSwicGlfYiI6W1siMjQ3ODQ1NTExMzM0NjYwOTcwNzU4ODgzODIyMjQ2Mzc4NTU0NjQwOTE0MDIyNjAwMzQ3MDU2NzE1NDU4NjY1NTgzODk1OTE2NDYyNiIsIjkyOTMwMTk0MDA4NDcyNTI0MjMyODk3Njk1NDQ2MTE0NjU4ODE0OTEyNzY3ODA3OTI5ODk2NDc3NzI3MjE1ODIwNzY2NzY2NjUyMTIiXSxbIjM4NTkyMzg1NjUxOTQ3OTQ4NjUxNTM3NDU0NDUzNzA3NzYyMjM2NjgwNjQ4MTk1Nzk4MjczMjkyMTc5MjM2NDk4MDg0NTU5OTEzMjYiLCIyMTg3MjI4OTA2MDg0MTE4MDM3NDI3NTAwMTA5Mjg4OTY2NzQ2NzUyMzkxMDI2NTIyMDQxODY1NDA3MDM0MDI0MDAxNDA4NDQxNjIxMyJdLFsiMSIsIjAiXV0sInBpX2MiOlsiODgxODAyMDA2NzQ5NDc5NDI5Njg2MDk3NjAyODE2MjU1OTg0OTM1Nzg1MjcwOTM2NTU5ODI2OTI5NDAxNzEzODEyMDY0MDk2NTg2NCIsIjE3OTcyNDU1NzY5MzUwNzkxMTE3OTQwMjA3NjM3NjU0ODYxOTA4ODk5MjAwMzI3NjA3ODUzNzY4NjYxMTE1ODIyOTAwNzU0NDk3OTQzIiwiMSJdLCJwcm90b2NvbCI6Imdyb3RoMTYiLCJjdXJ2ZSI6ImJuMTI4In0sInB1Yl9zaWduYWxzIjpbIjIxNTc1MTI3MjE2MjM2MjQ4ODY5NzAyMjc2MjQ2MDM3NTU3MTE5MDA3NDY2MTgwMzAxOTU3NzYyMTk2NTkzNzg2NzMzMDA3NjE3IiwiMjQ3MDc1MTE5NDk3NTU4Njk5NzU5ODQzMTI5NzMyMzc4NTAwNzIwMDE4NTk2NTMzNTc1ODc1OTA2MTgxNjM1NDgyNjc1MjY5MTA5MSIsIjE3ODQ5OTgxNzIwNjM0MjEyODAyNjY0MTg5OTI5NjcxMTQwNzYyNTU3NDgzMjM2NzA4MDk3NzIzODg0MTcyNjQxMTI0NjkxMjIyMjk4Il19'; 413 | 414 | await expect(verifier.fullVerify(token, request, testOpts)).resolves.not.toThrow(); 415 | }); 416 | 417 | it('TestVerifyV3MessageWithMtpProof_Merklized_exists', async () => { 418 | const request: AuthorizationRequestMessage = { 419 | id: '7e5b5847-b479-4499-90ee-5fe4826a5bdd', 420 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 421 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, 422 | thid: '7e5b5847-b479-4499-90ee-5fe4826a5bdd', 423 | body: { 424 | callbackUrl: 'https://test.com/callback', 425 | reason: 'test', 426 | scope: [ 427 | { 428 | id: 1711522489, 429 | circuitId: CircuitId.AtomicQueryV3, 430 | 431 | query: { 432 | allowedIssuers: ['*'], 433 | context: 434 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v4.jsonld', 435 | credentialSubject: { birthday: { $exists: true } }, 436 | proofType: ProofType.BJJSignature, 437 | type: 'KYCAgeCredential' 438 | } 439 | } 440 | ] 441 | }, 442 | from: 'did:polygonid:polygon:mumbai:2qH7TstpRRJHXNN4o49Fu9H2Qismku8hQeUxDVrjqT' 443 | }; 444 | 445 | const message: AuthorizationResponseMessage = { 446 | id: 'ac381820-21af-499a-8c5d-8f01fca9783c', 447 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 448 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_RESPONSE_MESSAGE_TYPE, 449 | thid: '7e5b5847-b479-4499-90ee-5fe4826a5bdd', 450 | body: { 451 | scope: [ 452 | { 453 | id: 1711522489, 454 | circuitId: CircuitId.AtomicQueryV3, 455 | proof: { 456 | pi_a: [ 457 | '21800211430949703449644722551376257237362982820810967048456391134029879678806', 458 | '21613713430915613066339120095996323766151016451049155252578200709882864737789', 459 | '1' 460 | ], 461 | pi_b: [ 462 | [ 463 | '661072511254964853872611502929686343046899584048079678556280335611662815845', 464 | '2273089975406654115307642615483414515773518585010287695430027009913825128768' 465 | ], 466 | [ 467 | '17444892701184321994625361553850698475151625663824663639633423947771970343321', 468 | '15630676073412856608625380437792507729110631716538445053251013755584497527789' 469 | ], 470 | ['1', '0'] 471 | ], 472 | pi_c: [ 473 | '21296780288267664754328313860061774394253634460478952036284913832000895592194', 474 | '6946412759922081597032874175095790261523599890928317338700002333834382713410', 475 | '1' 476 | ], 477 | protocol: 'groth16' 478 | }, 479 | pub_signals: [ 480 | '1', 481 | '29164643842236980629969889601908506056905540530259492186963618550155186690', 482 | '13483594486393726782589954979757194488582220051583949915340451442108840786819', 483 | '0', 484 | '0', 485 | '0', 486 | '1', 487 | '1711522489', 488 | '20140537885605785819118769494650292165307016100986347572517912906305442306', 489 | '1', 490 | '14623770256788200718910247650869365371230778783613615087356654172486552332511', 491 | '1711522550', 492 | '267831521922558027206082390043321796944', 493 | '20376033832371109177683048456014525905119173674985843915445634726167450989630', 494 | '0', 495 | '11', 496 | '1', 497 | '0', 498 | '0', 499 | '0', 500 | '0', 501 | '0', 502 | '0', 503 | '0', 504 | '0', 505 | '0', 506 | '0', 507 | '0', 508 | '0', 509 | '0', 510 | '0', 511 | '0', 512 | '0', 513 | '0', 514 | '0', 515 | '0', 516 | '0', 517 | '0', 518 | '0', 519 | '0', 520 | '0', 521 | '0', 522 | '0', 523 | '0', 524 | '0', 525 | '0', 526 | '0', 527 | '0', 528 | '0', 529 | '0', 530 | '0', 531 | '0', 532 | '0', 533 | '0', 534 | '0', 535 | '0', 536 | '0', 537 | '0', 538 | '0', 539 | '0', 540 | '0', 541 | '0', 542 | '0', 543 | '0', 544 | '0', 545 | '0', 546 | '0', 547 | '0', 548 | '0', 549 | '0', 550 | '0', 551 | '0', 552 | '0', 553 | '0', 554 | '0', 555 | '0', 556 | '0', 557 | '0', 558 | '0', 559 | '0', 560 | '1', 561 | '19077537563018779797836314438360413772747104486842143844867680645228663298', 562 | '0' 563 | ] 564 | } 565 | ] 566 | }, 567 | from: 'did:polygonid:polygon:mumbai:2qE45yJ5i6g1dYPP45KMw38Xbvh8aebPzfXLfxtrhu', 568 | to: 'did:polygonid:polygon:mumbai:2qH7TstpRRJHXNN4o49Fu9H2Qismku8hQeUxDVrjqT' 569 | }; 570 | 571 | const authInstance = await Verifier.newVerifier({ 572 | stateResolver: resolvers, 573 | circuitsDir: getTestDataPath('./testdata') 574 | }); 575 | 576 | await authInstance.verifyAuthResponse(message, request, testOpts); 577 | }); 578 | 579 | it('TestVerifyV3MessageWithMtpProof_Merklized_noop', async () => { 580 | const request: AuthorizationRequestMessage = { 581 | id: '7e5b5847-b479-4499-90ee-5fe4826a5bdd', 582 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 583 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE, 584 | thid: '7e5b5847-b479-4499-90ee-5fe4826a5bdd', 585 | body: { 586 | callbackUrl: 'https://test.com/callback', 587 | reason: 'test', 588 | scope: [ 589 | { 590 | id: 1711377832, 591 | circuitId: CircuitId.AtomicQueryV3, 592 | 593 | query: { 594 | allowedIssuers: ['*'], 595 | context: 596 | 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v4.jsonld', 597 | proofType: ProofType.BJJSignature, 598 | type: 'KYCAgeCredential' 599 | } 600 | } 601 | ] 602 | }, 603 | from: 'did:polygonid:polygon:mumbai:2qHwoMVgF22ozYfs4gXiC8rr6S3sBCr2WSQwkRTfB3' 604 | }; 605 | 606 | const message: AuthorizationResponseMessage = { 607 | id: 'ac381820-21af-499a-8c5d-8f01fca9783c', 608 | typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage, 609 | type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_RESPONSE_MESSAGE_TYPE, 610 | thid: '7e5b5847-b479-4499-90ee-5fe4826a5bdd', 611 | body: { 612 | scope: [ 613 | { 614 | id: 1711377832, 615 | circuitId: CircuitId.AtomicQueryV3, 616 | proof: { 617 | pi_a: [ 618 | '7555135671567273543704218764274850015720432713673229342215169366176898598929', 619 | '5118059757222184178232295484120895788454978441731084933033870844411500481244', 620 | '1' 621 | ], 622 | pi_b: [ 623 | [ 624 | '14212250237160868782979223012368501182246698067654154738920528940031235088380', 625 | '18217717461197738936131417405953772342169705587070066489162095180660073064671' 626 | ], 627 | [ 628 | '16772018071565641000678496564873785294025025924126602838983639773623869064801', 629 | '4936938093592837071503468878843020708039523542423061890804250451547565506589' 630 | ], 631 | ['1', '0'] 632 | ], 633 | pi_c: [ 634 | '4721635046811376766778210935918363175751512711098010569765488285431776823966', 635 | '20588271981362351104785915980589379197992461314597051622104515944586759543278', 636 | '1' 637 | ], 638 | protocol: 'groth16' 639 | }, 640 | pub_signals: [ 641 | '1', 642 | '20156969113212549915290105481651790678263685077956855660838446742358921730', 643 | '13483594486393726782589954979757194488582220051583949915340451442108840786819', 644 | '0', 645 | '0', 646 | '0', 647 | '1', 648 | '1711377832', 649 | '20140537885605785819118769494650292165307016100986347572517912906305442306', 650 | '1', 651 | '20054680232313097776046407964659487432368209966036937175997819086186177614781', 652 | '1711377852', 653 | '267831521922558027206082390043321796944', 654 | '0', 655 | '0', 656 | '0', 657 | '0', 658 | '0', 659 | '0', 660 | '0', 661 | '0', 662 | '0', 663 | '0', 664 | '0', 665 | '0', 666 | '0', 667 | '0', 668 | '0', 669 | '0', 670 | '0', 671 | '0', 672 | '0', 673 | '0', 674 | '0', 675 | '0', 676 | '0', 677 | '0', 678 | '0', 679 | '0', 680 | '0', 681 | '0', 682 | '0', 683 | '0', 684 | '0', 685 | '0', 686 | '0', 687 | '0', 688 | '0', 689 | '0', 690 | '0', 691 | '0', 692 | '0', 693 | '0', 694 | '0', 695 | '0', 696 | '0', 697 | '0', 698 | '0', 699 | '0', 700 | '0', 701 | '0', 702 | '0', 703 | '0', 704 | '0', 705 | '0', 706 | '0', 707 | '0', 708 | '0', 709 | '0', 710 | '0', 711 | '0', 712 | '0', 713 | '0', 714 | '0', 715 | '0', 716 | '0', 717 | '0', 718 | '0', 719 | '0', 720 | '0', 721 | '0', 722 | '19077537563018779797836314438360413772747104486842143844867680645228663298', 723 | '0' 724 | ] 725 | } 726 | ] 727 | }, 728 | from: 'did:polygonid:polygon:mumbai:2qK9EukpMd6GQy9hfXfX31LUk89rmqX21hYe62LEnW', 729 | to: 'did:polygonid:polygon:mumbai:2qHwoMVgF22ozYfs4gXiC8rr6S3sBCr2WSQwkRTfB3' 730 | }; 731 | 732 | const authInstance = await Verifier.newVerifier({ 733 | stateResolver: resolvers, 734 | circuitsDir: getTestDataPath('./testdata') 735 | }); 736 | 737 | await authInstance.verifyAuthResponse(message, request, testOpts); 738 | }); 739 | }); 740 | --------------------------------------------------------------------------------