/** * Indy Verifier Agent * Verifies Hyperledger Indy credentials and updates on-chain verifier contract */ import * as indy from 'indy-sdk'; import { ethers } from 'ethers'; export interface ProofRequest { name: string; version: string; requested_attributes: Record; requested_predicates: Record; } export interface IndyConfig { poolName: string; genesisPath: string; walletName: string; walletKey: string; did: string; verifierContractAddress: string; verifierAbi: any[]; } export class IndyVerifierAgent { private poolHandle: number | null = null; private walletHandle: number | null = null; private provider: ethers.JsonRpcProvider; private verifierContract: ethers.Contract; private signer: ethers.Wallet; constructor( private config: IndyConfig, rpcUrl: string ) { this.provider = new ethers.JsonRpcProvider(rpcUrl); this.verifierContract = new ethers.Contract( config.verifierContractAddress, config.verifierAbi, this.provider ); this.signer = new ethers.Wallet( process.env.INDY_VERIFIER_PRIVATE_KEY!, this.provider ); } /** * Initialize Indy pool and wallet */ async initialize(): Promise { // Create pool await indy.createPoolLedgerConfig(this.config.poolName, { genesis_txn: this.config.genesisPath }); this.poolHandle = await indy.openPoolLedger(this.config.poolName, null); // Create wallet try { await indy.createWallet( { id: this.config.walletName }, { key: this.config.walletKey } ); } catch (error: any) { if (!error.message.includes('already exists')) { throw error; } } this.walletHandle = await indy.openWallet( { id: this.config.walletName }, { key: this.config.walletKey } ); console.log('Indy pool and wallet initialized'); } /** * Verify credential proof */ async verifyCredentialProof( userAddress: string, proof: any, proofRequest: ProofRequest, schemas: any, credDefs: any ): Promise<{ verified: boolean; score: number }> { if (!this.poolHandle || !this.walletHandle) { throw new Error('Indy not initialized'); } // Verify the zero-knowledge proof const valid = await indy.verifierVerifyProof( proofRequest, proof, schemas, credDefs, {}, // revoc reg defs {} // revoc regs ); // Calculate score (0-100) based on proof validity and attributes const score = valid ? 100 : 0; // Update on-chain verifier contract if (valid) { const proofId = ethers.id(JSON.stringify(proof)); const verifierWithSigner = this.verifierContract.connect(this.signer); // Determine credential type from proof request const credType = this.getCredentialType(proofRequest); await verifierWithSigner.verifyCredentialProof( userAddress, credType, ethers.AbiCoder.defaultAbiCoder().encode(['string'], [JSON.stringify(proof)]), score, proofId ); } return { verified: valid, score }; } /** * Create proof request for KYC */ createKYCProofRequest(): ProofRequest { return { name: 'Bridge KYC Verification', version: '1.0', requested_attributes: { 'attr1_referent': { name: 'kyc_status', restrictions: [{ schema_id: 'did:indy:sovrin:...:schema:kyc:1.0', cred_def_id: 'did:indy:sovrin:...:creddef:...' }] }, 'attr2_referent': { name: 'jurisdiction', restrictions: [] } }, requested_predicates: { 'predicate1_referent': { name: 'age', p_type: '>=', p_value: 18 } } }; } /** * Create proof request for accredited investor */ createAccreditedInvestorProofRequest(): ProofRequest { return { name: 'Accredited Investor Verification', version: '1.0', requested_attributes: { 'attr1_referent': { name: 'accredited_status', restrictions: [{ schema_id: 'did:indy:sovrin:...:schema:accredited:1.0', cred_def_id: 'did:indy:sovrin:...:creddef:...' }] } }, requested_predicates: {} }; } /** * Get credential type from proof request */ private getCredentialType(proofRequest: ProofRequest): number { if (proofRequest.name.includes('KYC')) { return 0; // KYC } else if (proofRequest.name.includes('Accredited')) { return 1; // AccreditedInvestor } else if (proofRequest.name.includes('Institution')) { return 2; // Institution } else if (proofRequest.name.includes('Jurisdiction')) { return 3; // Jurisdiction } return 4; // Compliance } /** * Close wallet and pool */ async close(): Promise { if (this.walletHandle) { await indy.closeWallet(this.walletHandle); this.walletHandle = null; } if (this.poolHandle) { await indy.closePoolLedger(this.poolHandle); this.poolHandle = null; } } }