@cheny56/zk-kyc-did 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,243 @@
1
+ /**
2
+ * KYC/DID Client SDK
3
+ *
4
+ * High-level client for interacting with credential verification contracts
5
+ * and generating ZK proofs for credential predicates.
6
+ */
7
+
8
+ const { ethers } = require('ethers');
9
+ const {
10
+ VerifiableCredential,
11
+ CredentialWallet,
12
+ Predicate,
13
+ PredicateOp,
14
+ generateProofInputs,
15
+ preparePublicInputs,
16
+ } = require('../lib');
17
+
18
+ // Try to import snarkjs (optional for demo mode)
19
+ let snarkjs = null;
20
+ try {
21
+ snarkjs = require('snarkjs');
22
+ } catch (e) {
23
+ console.warn('snarkjs not available - running in demo mode');
24
+ }
25
+
26
+ /**
27
+ * KYC Client for credential verification
28
+ */
29
+ class KYCClient {
30
+ /**
31
+ * @param {Object} options
32
+ * @param {object} options.provider Ethers provider
33
+ * @param {object} options.signer Ethers signer
34
+ * @param {string} options.verifierAddress CredentialVerifier contract address
35
+ * @param {string} [options.proveKeysDir] Directory containing proving keys
36
+ * @param {boolean} [options.demoMode] Skip proof generation (for testing)
37
+ */
38
+ constructor(options) {
39
+ this.provider = options.provider;
40
+ this.signer = options.signer;
41
+ this.verifierAddress = options.verifierAddress;
42
+ this.proveKeysDir = options.proveKeysDir || './keys';
43
+ this.demoMode = options.demoMode || !snarkjs;
44
+
45
+ this.contract = null;
46
+ this.wallet = null;
47
+ }
48
+
49
+ /**
50
+ * Initialize the client
51
+ * @param {CredentialWallet} [wallet] Optional credential wallet
52
+ */
53
+ async init(wallet = null) {
54
+ if (wallet) {
55
+ this.wallet = wallet;
56
+ } else {
57
+ this.wallet = new CredentialWallet();
58
+ await this.wallet.init();
59
+ }
60
+
61
+ // Initialize credential library
62
+ await require('../lib').init();
63
+
64
+ console.log('KYC Client initialized');
65
+ console.log(` Demo mode: ${this.demoMode}`);
66
+ console.log(` Subject DID: ${this.wallet.subjectDID}`);
67
+
68
+ return this;
69
+ }
70
+
71
+ /**
72
+ * Set the verifier contract instance
73
+ * @param {object} contract Ethers contract instance
74
+ */
75
+ setContract(contract) {
76
+ this.contract = contract;
77
+ }
78
+
79
+ /**
80
+ * Create a credential (issuer side)
81
+ * @param {Object} options
82
+ * @param {string} options.subjectDID Subject's DID
83
+ * @param {Object} options.claims Claims object
84
+ * @param {string} [options.type] Credential type
85
+ * @param {bigint} issuerPrivateKey Issuer's private key
86
+ * @returns {Promise<VerifiableCredential>}
87
+ */
88
+ async createCredential(options, issuerPrivateKey) {
89
+ const credential = new VerifiableCredential({
90
+ issuer: this.signer.address, // Or issuer's DID
91
+ subject: options.subjectDID,
92
+ claims: options.claims,
93
+ type: options.type || 'KYC',
94
+ });
95
+
96
+ await credential.init();
97
+ credential.sign(BigInt(issuerPrivateKey));
98
+
99
+ return credential;
100
+ }
101
+
102
+ /**
103
+ * Add a credential to wallet
104
+ * @param {VerifiableCredential} credential
105
+ */
106
+ addCredential(credential) {
107
+ this.wallet.addCredential(credential);
108
+ }
109
+
110
+ /**
111
+ * Generate a ZK proof for credential predicates
112
+ * @param {string} credentialId Credential ID
113
+ * @param {Predicate[]} predicates Predicates to prove
114
+ * @returns {Promise<Object>} { proof, publicInputs }
115
+ */
116
+ async generateProof(credentialId, predicates) {
117
+ const credential = this.wallet.getCredential(credentialId);
118
+ if (!credential) {
119
+ throw new Error(`Credential ${credentialId} not found`);
120
+ }
121
+
122
+ // Check credential not expired
123
+ if (credential.isExpired()) {
124
+ throw new Error('Credential has expired');
125
+ }
126
+
127
+ // Generate proof inputs
128
+ const privateInputs = await generateProofInputs(credential, predicates);
129
+ const publicInputs = preparePublicInputs(predicates, credential.issuer);
130
+
131
+ // Generate ZK proof (or skip in demo mode)
132
+ let proof = null;
133
+ if (!this.demoMode) {
134
+ proof = await this._generateZKProof(privateInputs, publicInputs);
135
+ }
136
+
137
+ return {
138
+ proof,
139
+ publicInputs,
140
+ credentialRoot: this._computeCredentialRoot(credential),
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Verify a credential on-chain
146
+ * @param {Object} proofData Proof data from generateProof
147
+ * @returns {Promise<{tx: object, verified: boolean}>}
148
+ */
149
+ async verifyOnChain(proofData) {
150
+ if (!this.contract) {
151
+ throw new Error('Contract not set. Call setContract() first.');
152
+ }
153
+
154
+ if (this.demoMode) {
155
+ // Skip proof verification in demo mode
156
+ const tx = await this.contract.verifyCredentialBasic(
157
+ BigInt(proofData.publicInputs.issuerPublicKey),
158
+ proofData.credentialRoot
159
+ );
160
+ return { verified: await tx, tx: null };
161
+ }
162
+
163
+ // Format proof for contract
164
+ const formattedProof = this._formatProof(proofData.proof);
165
+
166
+ // Call contract
167
+ const tx = await this.contract.verifyCredential(
168
+ formattedProof.pA,
169
+ formattedProof.pB,
170
+ formattedProof.pC,
171
+ BigInt(proofData.publicInputs.issuerPublicKey),
172
+ BigInt(proofData.publicInputs.predicateCommitments || 0),
173
+ proofData.credentialRoot
174
+ );
175
+
176
+ await tx.wait();
177
+
178
+ return { verified: true, tx };
179
+ }
180
+
181
+ /**
182
+ * Check if an issuer is trusted
183
+ * @param {string} issuerAddress Issuer address
184
+ * @returns {Promise<boolean>}
185
+ */
186
+ async isIssuerTrusted(issuerAddress) {
187
+ if (!this.contract) {
188
+ throw new Error('Contract not set');
189
+ }
190
+ return this.contract.trustedIssuers(issuerAddress);
191
+ }
192
+
193
+ /**
194
+ * Get wallet
195
+ * @returns {CredentialWallet}
196
+ */
197
+ getWallet() {
198
+ return this.wallet;
199
+ }
200
+
201
+ // ============================================
202
+ // Private methods
203
+ // ============================================
204
+
205
+ async _generateZKProof(privateInputs, publicInputs) {
206
+ if (!snarkjs) throw new Error('snarkjs not available');
207
+
208
+ // This would use the actual circuit
209
+ // For now, return a placeholder structure
210
+ const { proof, publicSignals } = await snarkjs.groth16.fullProve(
211
+ privateInputs,
212
+ `${this.proveKeysDir}/credential-proof_js/credential-proof.wasm`,
213
+ `${this.proveKeysDir}/credential-proof_final.zkey`
214
+ );
215
+
216
+ return { proof, publicSignals };
217
+ }
218
+
219
+ _formatProof(proof) {
220
+ if (!proof) return null;
221
+
222
+ return {
223
+ pA: [proof.pi_a[0], proof.pi_a[1]],
224
+ pB: [
225
+ [proof.pi_b[0][1], proof.pi_b[0][0]],
226
+ [proof.pi_b[1][1], proof.pi_b[1][0]],
227
+ ],
228
+ pC: [proof.pi_c[0], proof.pi_c[1]],
229
+ };
230
+ }
231
+
232
+ _computeCredentialRoot(credential) {
233
+ const { poseidonHash } = require('../lib');
234
+ const commitments = Object.values(credential.claimCommitments).sort();
235
+ return poseidonHash([...commitments, BigInt(credential.issuer)]);
236
+ }
237
+ }
238
+
239
+ module.exports = {
240
+ KYCClient,
241
+ Predicate,
242
+ PredicateOp,
243
+ };
@@ -0,0 +1,119 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ /**
5
+ * @title CredentialVerifier
6
+ * @notice Verifies ZK proofs for credential predicates
7
+ * @dev Uses external ZK verifier contract (generated by snarkjs)
8
+ */
9
+ contract CredentialVerifier {
10
+ // Verifier contract address (set in constructor)
11
+ address public verifier;
12
+
13
+ // Issuer registry (trusted issuers)
14
+ mapping(address => bool) public trustedIssuers;
15
+
16
+ // Events
17
+ event CredentialVerified(
18
+ address indexed verifier,
19
+ address indexed issuer,
20
+ bytes32 indexed credentialRoot,
21
+ uint256 timestamp
22
+ );
23
+
24
+ event IssuerAdded(address indexed issuer);
25
+ event IssuerRemoved(address indexed issuer);
26
+
27
+ /**
28
+ * @notice Deploy verifier contract
29
+ * @param _verifier Address of ZK verifier contract
30
+ */
31
+ constructor(address _verifier) {
32
+ verifier = _verifier;
33
+ }
34
+
35
+ /**
36
+ * @notice Add a trusted issuer (admin only)
37
+ * @param issuer Issuer's public key or address
38
+ */
39
+ function addTrustedIssuer(address issuer) external {
40
+ // In production, add access control
41
+ trustedIssuers[issuer] = true;
42
+ emit IssuerAdded(issuer);
43
+ }
44
+
45
+ /**
46
+ * @notice Remove a trusted issuer
47
+ * @param issuer Issuer to remove
48
+ */
49
+ function removeTrustedIssuer(address issuer) external {
50
+ trustedIssuers[issuer] = false;
51
+ emit IssuerRemoved(issuer);
52
+ }
53
+
54
+ /**
55
+ * @notice Verify a credential predicate proof
56
+ * @param proof ZK proof
57
+ * @param issuerPublicKey Issuer's public key
58
+ * @param predicateCommitments Hash of predicates
59
+ * @param credentialRoot Root commitment of credential
60
+ * @return True if proof is valid
61
+ */
62
+ function verifyCredential(
63
+ uint256[2] calldata proofA,
64
+ uint256[2][2] calldata proofB,
65
+ uint256[2] calldata proofC,
66
+ uint256 issuerPublicKey,
67
+ uint256 predicateCommitments,
68
+ uint256 credentialRoot
69
+ ) external view returns (bool) {
70
+ // Check issuer is trusted
71
+ require(trustedIssuers[address(uint160(issuerPublicKey))], "Issuer not trusted");
72
+
73
+ // Prepare public inputs
74
+ uint256[3] memory publicInputs = [
75
+ issuerPublicKey,
76
+ predicateCommitments,
77
+ credentialRoot
78
+ ];
79
+
80
+ // Call verifier contract
81
+ (bool success, bytes memory result) = verifier.staticcall(
82
+ abi.encodeWithSignature(
83
+ "verifyProof(uint256[2],uint256[2][2],uint256[2],uint256[])",
84
+ proofA,
85
+ proofB,
86
+ proofC,
87
+ publicInputs
88
+ )
89
+ );
90
+
91
+ if (!success) return false;
92
+
93
+ bool verified = abi.decode(result, (bool));
94
+
95
+ if (verified) {
96
+ emit CredentialVerified(
97
+ msg.sender,
98
+ address(uint160(issuerPublicKey)),
99
+ bytes32(credentialRoot),
100
+ block.timestamp
101
+ );
102
+ }
103
+
104
+ return verified;
105
+ }
106
+
107
+ /**
108
+ * @notice Verify credential without proof (for testing)
109
+ * @param issuerPublicKey Issuer's public key
110
+ * @param credentialRoot Credential root
111
+ * @return True if issuer is trusted
112
+ */
113
+ function verifyCredentialBasic(
114
+ uint256 issuerPublicKey,
115
+ uint256 credentialRoot
116
+ ) external view returns (bool) {
117
+ return trustedIssuers[address(uint160(issuerPublicKey))];
118
+ }
119
+ }
@@ -0,0 +1,23 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ /**
5
+ * @title IVerifier
6
+ * @notice Interface for ZK proof verifiers generated by snarkjs
7
+ */
8
+ interface IVerifier {
9
+ /**
10
+ * @notice Verify a Groth16 proof
11
+ * @param _pA First element of proof
12
+ * @param _pB Second element of proof (2x2 matrix)
13
+ * @param _pC Third element of proof
14
+ * @param _pubSignals Public inputs to the circuit
15
+ * @return True if proof is valid
16
+ */
17
+ function verifyProof(
18
+ uint256[2] calldata _pA,
19
+ uint256[2][2] calldata _pB,
20
+ uint256[2] calldata _pC,
21
+ uint256[] calldata _pubSignals
22
+ ) external view returns (bool);
23
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Create Credential Example
3
+ *
4
+ * Demonstrates how a KYC provider issues a credential to a user.
5
+ *
6
+ * Run: node examples/create-credential.js
7
+ */
8
+
9
+ const { VerifiableCredential, CredentialWallet, init } = require('../lib');
10
+
11
+ async function main() {
12
+ console.log('='.repeat(70));
13
+ console.log(' ZK KYC/DID - Create Credential Example');
14
+ console.log('='.repeat(70));
15
+ console.log();
16
+
17
+ // Initialize library
18
+ await init();
19
+
20
+ // ========================================
21
+ // Step 1: User creates wallet
22
+ // ========================================
23
+ console.log('STEP 1: User Creates Wallet');
24
+ console.log('-'.repeat(70));
25
+
26
+ const userWallet = new CredentialWallet();
27
+ await userWallet.init();
28
+
29
+ console.log(` User DID: ${userWallet.subjectDID}`);
30
+ console.log(` (length: ${userWallet.subjectDID.length})`);
31
+ console.log();
32
+
33
+ // ========================================
34
+ // Step 2: KYC Provider issues credential
35
+ // ========================================
36
+ console.log('STEP 2: KYC Provider Issues Credential');
37
+ console.log('-'.repeat(70));
38
+
39
+ // Simulate KYC provider
40
+ const kycProviderPrivateKey = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn;
41
+ const kycProviderPublicKey = '0x' + kycProviderPrivateKey.toString(16);
42
+
43
+ // User's KYC data (this would come from actual KYC process)
44
+ const kycData = {
45
+ age: 25,
46
+ country: 'US',
47
+ documentType: 'passport',
48
+ documentNumber: 'P123456', // This will be hidden in commitments
49
+ verified: true,
50
+ tier: 2, // KYC tier level
51
+ };
52
+
53
+ console.log(' KYC Data (will be hidden in credential):');
54
+ for (const [key, value] of Object.entries(kycData)) {
55
+ console.log(` ${key}: ${value}`);
56
+ }
57
+ console.log();
58
+
59
+ // Create credential
60
+ const credential = new VerifiableCredential({
61
+ issuer: kycProviderPublicKey,
62
+ subject: userWallet.subjectDID,
63
+ claims: kycData,
64
+ type: 'KYC',
65
+ expirationDate: Date.now() + 365 * 24 * 60 * 60 * 1000, // 1 year
66
+ });
67
+
68
+ await credential.init();
69
+ credential.sign(kycProviderPrivateKey);
70
+
71
+ console.log(' Credential Created:');
72
+ console.log(` ID: ${credential.id}`);
73
+ console.log(` Type: ${credential.type}`);
74
+ console.log(` Issuer: ${credential.issuer.slice(0, 30)}...`);
75
+ console.log(` Subject: ${credential.subject}`);
76
+ console.log(` Issued: ${new Date(credential.issuedAt).toISOString()}`);
77
+ console.log(` Expires: ${new Date(credential.expirationDate).toISOString()}`);
78
+ console.log();
79
+
80
+ // ========================================
81
+ // Step 3: Show commitments (public data)
82
+ // ========================================
83
+ console.log('STEP 3: Credential Commitments (Public)');
84
+ console.log('-'.repeat(70));
85
+
86
+ const commitments = credential.getAllCommitments();
87
+ console.log(' Commitments (visible on-chain, but values hidden):');
88
+ for (const [key, commitment] of Object.entries(commitments)) {
89
+ console.log(` ${key}: ${commitment.slice(0, 30)}... (len: ${commitment.length})`);
90
+ }
91
+ console.log();
92
+
93
+ // ========================================
94
+ // Step 4: User stores credential
95
+ // ========================================
96
+ console.log('STEP 4: User Stores Credential');
97
+ console.log('-'.repeat(70));
98
+
99
+ userWallet.addCredential(credential);
100
+
101
+ console.log(` Credential added to wallet`);
102
+ console.log(` Total credentials: ${userWallet.getAllCredentials().length}`);
103
+ console.log();
104
+
105
+ // ========================================
106
+ // Step 5: Verify signature
107
+ // ========================================
108
+ console.log('STEP 5: Verify Credential Signature');
109
+ console.log('-'.repeat(70));
110
+
111
+ const isValid = credential.verifySignature(BigInt(kycProviderPublicKey));
112
+ console.log(` Signature valid: ${isValid}`);
113
+ console.log(` Expired: ${credential.isExpired()}`);
114
+ console.log();
115
+
116
+ // ========================================
117
+ // Step 6: Export for backup
118
+ // ========================================
119
+ console.log('STEP 6: Export Credential (for backup)');
120
+ console.log('-'.repeat(70));
121
+
122
+ const exported = credential.toJSON();
123
+ const exportedStr = JSON.stringify(exported);
124
+ console.log(` Exported size: ${exportedStr.length} bytes`);
125
+ console.log(` Note: Actual claim values are NOT in export (privacy!)`);
126
+ console.log(` User must store claims separately if needed`);
127
+ console.log();
128
+
129
+ console.log('='.repeat(70));
130
+ console.log(' SUCCESS! Credential created and stored.');
131
+ console.log('='.repeat(70));
132
+ console.log();
133
+ console.log('Next steps:');
134
+ console.log(' 1. Run: node examples/generate-proof.js');
135
+ console.log(' 2. Run: node examples/verify-credential.js');
136
+ }
137
+
138
+ main().catch(err => {
139
+ console.error('Error:', err);
140
+ process.exit(1);
141
+ });
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Generate Proof Example
3
+ *
4
+ * Demonstrates how a user generates a ZK proof to prove credential predicates
5
+ * without revealing the actual claim values.
6
+ *
7
+ * Run: node examples/generate-proof.js
8
+ */
9
+
10
+ const {
11
+ CredentialWallet,
12
+ Predicate,
13
+ PredicateOp,
14
+ generateProofInputs,
15
+ preparePublicInputs,
16
+ init
17
+ } = require('../lib');
18
+
19
+ async function main() {
20
+ console.log('='.repeat(70));
21
+ console.log(' ZK KYC/DID - Generate Proof Example');
22
+ console.log('='.repeat(70));
23
+ console.log();
24
+
25
+ await init();
26
+
27
+ // ========================================
28
+ // Step 1: Load user's credential
29
+ // ========================================
30
+ console.log('STEP 1: Load User Credential');
31
+ console.log('-'.repeat(70));
32
+
33
+ // In a real scenario, this would be loaded from storage
34
+ // For demo, we'll create a sample credential
35
+ const wallet = new CredentialWallet();
36
+ await wallet.init();
37
+
38
+ // Create a sample KYC credential
39
+ const { VerifiableCredential } = require('../lib');
40
+ const credential = new VerifiableCredential({
41
+ issuer: '0x1234567890abcdef1234567890abcdef12345678',
42
+ subject: wallet.subjectDID,
43
+ claims: {
44
+ age: 25,
45
+ country: 'US',
46
+ documentType: 'passport',
47
+ verified: true,
48
+ tier: 2,
49
+ },
50
+ type: 'KYC',
51
+ });
52
+
53
+ await credential.init();
54
+ credential.sign(0xabcdefn); // Simplified signing
55
+
56
+ wallet.addCredential(credential);
57
+
58
+ console.log(` User DID: ${wallet.subjectDID}`);
59
+ console.log(` Credential ID: ${credential.id}`);
60
+ console.log(` Claims in credential: ${Object.keys(credential.claims).join(', ')}`);
61
+ console.log();
62
+
63
+ // ========================================
64
+ // Step 2: Define predicates to prove
65
+ // ========================================
66
+ console.log('STEP 2: Define Predicates to Prove');
67
+ console.log('-'.repeat(70));
68
+
69
+ // User wants to prove:
70
+ // 1. They are 18 or older (without revealing exact age)
71
+ // 2. They are from an allowed country (without revealing which)
72
+ // 3. They are verified (without revealing other details)
73
+
74
+ const predicates = [
75
+ new Predicate({
76
+ claimKey: 'age',
77
+ op: PredicateOp.GTE,
78
+ value: 18,
79
+ }),
80
+ new Predicate({
81
+ claimKey: 'country',
82
+ op: PredicateOp.IN,
83
+ value: ['US', 'UK', 'CA', 'EU'],
84
+ }),
85
+ new Predicate({
86
+ claimKey: 'verified',
87
+ op: PredicateOp.EQ,
88
+ value: true,
89
+ }),
90
+ ];
91
+
92
+ console.log(' Predicates to prove:');
93
+ for (const pred of predicates) {
94
+ console.log(` ${pred.claimKey} ${pred.op} ${JSON.stringify(pred.value)}`);
95
+ }
96
+ console.log();
97
+
98
+ // Verify predicates are satisfied
99
+ console.log(' Verifying predicates are satisfied:');
100
+ for (const pred of predicates) {
101
+ const claimValue = credential.claims[pred.claimKey];
102
+ const satisfied = pred.evaluate(claimValue);
103
+ console.log(` ${pred.claimKey}: ${claimValue} → ${satisfied ? '✓' : '✗'}`);
104
+ }
105
+ console.log();
106
+
107
+ // ========================================
108
+ // Step 3: Generate proof inputs
109
+ // ========================================
110
+ console.log('STEP 3: Generate Proof Inputs');
111
+ console.log('-'.repeat(70));
112
+
113
+ const privateInputs = await generateProofInputs(credential, predicates);
114
+ const publicInputs = preparePublicInputs(predicates, credential.issuer);
115
+
116
+ console.log(' Private Inputs (hidden in proof):');
117
+ console.log(` Claim values: ${Object.keys(privateInputs.claimValues).join(', ')}`);
118
+ console.log(` Blinding factors: ${Object.keys(privateInputs.blindingFactors).join(', ')}`);
119
+ console.log(` Signature: ${privateInputs.signature.slice(0, 20)}...`);
120
+ console.log();
121
+
122
+ console.log(' Public Inputs (visible in proof):');
123
+ console.log(` Issuer: ${publicInputs.issuerPublicKey.slice(0, 30)}...`);
124
+ console.log(` Predicates: ${publicInputs.predicates.length} predicates`);
125
+ for (const pred of publicInputs.predicates) {
126
+ console.log(` - ${pred.claimKey} ${pred.op} ${JSON.stringify(pred.value)}`);
127
+ }
128
+ console.log();
129
+
130
+ // ========================================
131
+ // Step 4: What the proof proves
132
+ // ========================================
133
+ console.log('STEP 4: What the ZK Proof Proves');
134
+ console.log('-'.repeat(70));
135
+
136
+ console.log(' The proof demonstrates:');
137
+ console.log(' 1. User has a valid credential from the issuer');
138
+ console.log(' 2. Credential signature is valid');
139
+ console.log(' 3. age >= 18 (without revealing actual age)');
140
+ console.log(' 4. country IN [US, UK, CA, EU] (without revealing which)');
141
+ console.log(' 5. verified == true');
142
+ console.log();
143
+ console.log(' WITHOUT revealing:');
144
+ console.log(' - Exact age (could be 18, 25, 50, etc.)');
145
+ console.log(' - Which country (US, UK, CA, or EU)');
146
+ console.log(' - Other claims (documentType, tier, etc.)');
147
+ console.log(' - Blinding factors');
148
+ console.log(' - Credential signature');
149
+ console.log();
150
+
151
+ // ========================================
152
+ // Step 5: Compute credential root
153
+ // ========================================
154
+ console.log('STEP 5: Compute Credential Root');
155
+ console.log('-'.repeat(70));
156
+
157
+ const { poseidonHash } = require('../lib');
158
+ const commitments = Object.values(credential.claimCommitments).sort();
159
+ const credentialRoot = poseidonHash([...commitments, BigInt(credential.issuer)]);
160
+
161
+ console.log(` Credential root: 0x${credentialRoot.toString(16).slice(0, 30)}...`);
162
+ console.log(` (This is the public commitment to the credential)`);
163
+ console.log();
164
+
165
+ console.log('='.repeat(70));
166
+ console.log(' SUCCESS! Proof inputs generated.');
167
+ console.log('='.repeat(70));
168
+ console.log();
169
+ console.log('Next step: Generate actual ZK proof using snarkjs and verify on-chain');
170
+ console.log(' Run: node examples/verify-credential.js');
171
+ }
172
+
173
+ main().catch(err => {
174
+ console.error('Error:', err);
175
+ process.exit(1);
176
+ });