@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.
- package/README.md +520 -0
- package/circuits/credential-proof.circom +108 -0
- package/client/index.d.ts +5 -0
- package/client/index.js +13 -0
- package/client/kyc-client.js +243 -0
- package/contracts/CredentialVerifier.sol +119 -0
- package/contracts/interfaces/IVerifier.sol +23 -0
- package/examples/create-credential.js +141 -0
- package/examples/generate-proof.js +176 -0
- package/examples/verify-credential.js +150 -0
- package/examples/verify-setup.js +177 -0
- package/lib/credential.js +366 -0
- package/lib/index.d.ts +212 -0
- package/lib/index.js +39 -0
- package/lib/predicate.js +209 -0
- package/package.json +80 -0
|
@@ -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
|
+
});
|