@0xpolygonid/js-sdk 1.6.1 → 1.7.1
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 +3 -1
- package/dist/browser/esm/index.js +837 -494
- package/dist/browser/esm/index.js.map +1 -1
- package/dist/browser/umd/index.js +885 -536
- package/dist/browser/umd/index.js.map +1 -1
- package/dist/node/cjs/circuits/comparer.js +21 -3
- package/dist/node/cjs/circuits/linked-multi-query.js +1 -1
- package/dist/node/cjs/credentials/credential-wallet.js +20 -2
- package/dist/node/cjs/credentials/index.js +1 -0
- package/dist/node/cjs/credentials/rhs.js +28 -15
- package/dist/node/cjs/credentials/status/credential-status-publisher.js +84 -0
- package/dist/node/cjs/credentials/utils.js +14 -1
- package/dist/node/cjs/iden3comm/constants.js +2 -0
- package/dist/node/cjs/iden3comm/handlers/fetch.js +2 -2
- package/dist/node/cjs/iden3comm/handlers/index.js +1 -0
- package/dist/node/cjs/iden3comm/handlers/refresh.js +76 -0
- package/dist/node/cjs/identity/identity-wallet.js +57 -26
- package/dist/node/cjs/storage/blockchain/abi/CredentialStatusResolver.json +1 -1
- package/dist/node/cjs/storage/blockchain/index.js +1 -0
- package/dist/node/cjs/storage/blockchain/onchain-revocation.js +45 -5
- package/dist/node/cjs/verifiable/constants.js +10 -1
- package/dist/node/esm/circuits/comparer.js +21 -3
- package/dist/node/esm/circuits/linked-multi-query.js +1 -1
- package/dist/node/esm/credentials/credential-wallet.js +21 -3
- package/dist/node/esm/credentials/index.js +1 -0
- package/dist/node/esm/credentials/rhs.js +26 -14
- package/dist/node/esm/credentials/status/credential-status-publisher.js +78 -0
- package/dist/node/esm/credentials/utils.js +12 -0
- package/dist/node/esm/iden3comm/constants.js +2 -0
- package/dist/node/esm/iden3comm/handlers/fetch.js +2 -2
- package/dist/node/esm/iden3comm/handlers/index.js +1 -0
- package/dist/node/esm/iden3comm/handlers/refresh.js +72 -0
- package/dist/node/esm/identity/identity-wallet.js +52 -21
- package/dist/node/esm/storage/blockchain/abi/CredentialStatusResolver.json +1 -1
- package/dist/node/esm/storage/blockchain/index.js +1 -0
- package/dist/node/esm/storage/blockchain/onchain-revocation.js +45 -5
- package/dist/node/esm/verifiable/constants.js +9 -0
- package/dist/types/credentials/credential-wallet.d.ts +5 -1
- package/dist/types/credentials/index.d.ts +1 -0
- package/dist/types/credentials/rhs.d.ts +11 -0
- package/dist/types/credentials/status/credential-status-publisher.d.ts +70 -0
- package/dist/types/credentials/utils.d.ts +2 -0
- package/dist/types/iden3comm/constants.d.ts +1 -0
- package/dist/types/iden3comm/handlers/fetch.d.ts +2 -2
- package/dist/types/iden3comm/handlers/index.d.ts +1 -0
- package/dist/types/iden3comm/handlers/refresh.d.ts +56 -0
- package/dist/types/iden3comm/types/packer.d.ts +2 -1
- package/dist/types/iden3comm/types/protocol/credentials.d.ts +15 -0
- package/dist/types/identity/identity-wallet.d.ts +42 -13
- package/dist/types/storage/blockchain/index.d.ts +1 -0
- package/dist/types/storage/blockchain/onchain-revocation.d.ts +7 -3
- package/dist/types/verifiable/constants.d.ts +8 -0
- package/dist/types/verifiable/credential.d.ts +2 -1
- package/dist/types/verifiable/proof.d.ts +10 -1
- package/package.json +1 -1
|
@@ -22,9 +22,16 @@ class OnChainRevocationStorage {
|
|
|
22
22
|
* @param {string} - onhcain contract address
|
|
23
23
|
* @param {string} - rpc url to connect to the blockchain
|
|
24
24
|
*/
|
|
25
|
-
constructor(
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
25
|
+
constructor(_config, contractAddress, _signer) {
|
|
26
|
+
this._config = _config;
|
|
27
|
+
this._signer = _signer;
|
|
28
|
+
this._provider = new ethers_1.JsonRpcProvider(_config.url);
|
|
29
|
+
let contract = new ethers_1.Contract(contractAddress, CredentialStatusResolver_json_1.default, this._provider);
|
|
30
|
+
if (this._signer) {
|
|
31
|
+
this._signer = this._signer.connect(this._provider);
|
|
32
|
+
contract = contract.connect(this._signer);
|
|
33
|
+
}
|
|
34
|
+
this._contract = contract;
|
|
28
35
|
}
|
|
29
36
|
/**
|
|
30
37
|
* Get revocation status by issuerId, issuerState and nonce from the onchain.
|
|
@@ -32,7 +39,7 @@ class OnChainRevocationStorage {
|
|
|
32
39
|
* @returns Promise<RevocationStatus>
|
|
33
40
|
*/
|
|
34
41
|
async getRevocationStatusByIdAndState(issuerID, state, nonce) {
|
|
35
|
-
const response = await this.
|
|
42
|
+
const response = await this._contract.getRevocationStatusByIdAndState(issuerID, state, nonce);
|
|
36
43
|
const issuer = OnChainRevocationStorage.convertIssuerInfo(response.issuer);
|
|
37
44
|
const mtp = OnChainRevocationStorage.convertSmtProofToProof(response.mtp);
|
|
38
45
|
return {
|
|
@@ -46,7 +53,7 @@ class OnChainRevocationStorage {
|
|
|
46
53
|
* @returns Promise<RevocationStatus>
|
|
47
54
|
*/
|
|
48
55
|
async getRevocationStatus(issuerID, nonce) {
|
|
49
|
-
const response = await this.
|
|
56
|
+
const response = await this._contract.getRevocationStatus(issuerID, nonce);
|
|
50
57
|
const issuer = OnChainRevocationStorage.convertIssuerInfo(response.issuer);
|
|
51
58
|
const mtp = OnChainRevocationStorage.convertSmtProofToProof(response.mtp);
|
|
52
59
|
return {
|
|
@@ -54,6 +61,39 @@ class OnChainRevocationStorage {
|
|
|
54
61
|
mtp
|
|
55
62
|
};
|
|
56
63
|
}
|
|
64
|
+
async saveNodes(payload) {
|
|
65
|
+
if (!this._signer) {
|
|
66
|
+
throw new Error('No signer provided');
|
|
67
|
+
}
|
|
68
|
+
const feeData = await this._provider.getFeeData();
|
|
69
|
+
const maxFeePerGas = this._config.maxFeePerGas
|
|
70
|
+
? BigInt(this._config.maxFeePerGas)
|
|
71
|
+
: feeData.maxFeePerGas;
|
|
72
|
+
const maxPriorityFeePerGas = this._config.maxPriorityFeePerGas
|
|
73
|
+
? BigInt(this._config.maxPriorityFeePerGas)
|
|
74
|
+
: feeData.maxPriorityFeePerGas;
|
|
75
|
+
const gasLimit = await this._contract.saveNodes.estimateGas(payload);
|
|
76
|
+
const txData = await this._contract.saveNodes.populateTransaction(payload);
|
|
77
|
+
const request = {
|
|
78
|
+
to: txData.to,
|
|
79
|
+
data: txData.data,
|
|
80
|
+
gasLimit,
|
|
81
|
+
maxFeePerGas,
|
|
82
|
+
maxPriorityFeePerGas
|
|
83
|
+
};
|
|
84
|
+
const tx = await this._signer.sendTransaction(request);
|
|
85
|
+
return tx.wait().then((txReceipt) => {
|
|
86
|
+
if (!txReceipt) {
|
|
87
|
+
throw new Error(`transaction: ${tx.hash} failed to mine`);
|
|
88
|
+
}
|
|
89
|
+
const status = txReceipt.status;
|
|
90
|
+
const txnHash = txReceipt.hash;
|
|
91
|
+
if (!status) {
|
|
92
|
+
throw new Error(`transaction: ${txnHash} failed to mine`);
|
|
93
|
+
}
|
|
94
|
+
return txReceipt;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
57
97
|
static convertIssuerInfo(issuer) {
|
|
58
98
|
const [state, claimsTreeRoot, revocationTreeRoot, rootOfRoots] = issuer.map((i) => js_merkletree_1.Hash.fromBigInt(i).hex());
|
|
59
99
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SubjectPosition = exports.MerklizedRootPosition = exports.ProofPurpose = exports.CredentialStatusType = exports.ProofType = exports.VerifiableConstants = void 0;
|
|
3
|
+
exports.RefreshServiceType = exports.SubjectPosition = exports.MerklizedRootPosition = exports.ProofPurpose = exports.CredentialStatusType = exports.ProofType = exports.VerifiableConstants = void 0;
|
|
4
4
|
/** Verifiable constants */
|
|
5
5
|
exports.VerifiableConstants = Object.freeze({
|
|
6
6
|
ERRORS: {
|
|
@@ -103,3 +103,12 @@ var SubjectPosition;
|
|
|
103
103
|
// Value save subject in value part of claim.
|
|
104
104
|
SubjectPosition["Value"] = "value";
|
|
105
105
|
})(SubjectPosition = exports.SubjectPosition || (exports.SubjectPosition = {}));
|
|
106
|
+
/**
|
|
107
|
+
* RefreshServiceType type for refreshService
|
|
108
|
+
*
|
|
109
|
+
* @enum {string}
|
|
110
|
+
*/
|
|
111
|
+
var RefreshServiceType;
|
|
112
|
+
(function (RefreshServiceType) {
|
|
113
|
+
RefreshServiceType["Iden3RefreshService2023"] = "Iden3RefreshService2023";
|
|
114
|
+
})(RefreshServiceType = exports.RefreshServiceType || (exports.RefreshServiceType = {}));
|
|
@@ -50,12 +50,30 @@ export const QueryOperators = {
|
|
|
50
50
|
};
|
|
51
51
|
const allOperations = Object.values(QueryOperators);
|
|
52
52
|
export const availableTypesOperators = new Map([
|
|
53
|
-
[XSDNS.Boolean, [QueryOperators.$eq, QueryOperators.$ne]],
|
|
53
|
+
[XSDNS.Boolean, [QueryOperators.$eq, QueryOperators.$ne, QueryOperators.$sd]],
|
|
54
54
|
[XSDNS.Integer, allOperations],
|
|
55
55
|
[XSDNS.NonNegativeInteger, allOperations],
|
|
56
56
|
[XSDNS.PositiveInteger, allOperations],
|
|
57
|
-
[
|
|
58
|
-
|
|
57
|
+
[
|
|
58
|
+
XSDNS.Double,
|
|
59
|
+
[
|
|
60
|
+
QueryOperators.$eq,
|
|
61
|
+
QueryOperators.$ne,
|
|
62
|
+
QueryOperators.$in,
|
|
63
|
+
QueryOperators.$nin,
|
|
64
|
+
QueryOperators.$sd
|
|
65
|
+
]
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
XSDNS.String,
|
|
69
|
+
[
|
|
70
|
+
QueryOperators.$eq,
|
|
71
|
+
QueryOperators.$ne,
|
|
72
|
+
QueryOperators.$in,
|
|
73
|
+
QueryOperators.$nin,
|
|
74
|
+
QueryOperators.$sd
|
|
75
|
+
]
|
|
76
|
+
],
|
|
59
77
|
[XSDNS.DateTime, allOperations]
|
|
60
78
|
]);
|
|
61
79
|
/**
|
|
@@ -25,7 +25,7 @@ export class LinkedMultiQueryInputs extends BaseConfig {
|
|
|
25
25
|
if (!this.query[i]) {
|
|
26
26
|
enabled.push(0);
|
|
27
27
|
claimPathNotExists.push(0);
|
|
28
|
-
claimPathMtp.push(
|
|
28
|
+
claimPathMtp.push(new Array(this.getMTLevelsClaim()).fill('0'));
|
|
29
29
|
claimPathMtpNoAux.push('0');
|
|
30
30
|
claimPathMtpAuxHi.push('0');
|
|
31
31
|
claimPathMtpAuxHv.push('0');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DID } from '@iden3/js-iden3-core';
|
|
1
|
+
import { DID, getChainId } from '@iden3/js-iden3-core';
|
|
2
2
|
import { W3CCredential, VerifiableConstants, CredentialStatusType } from './../verifiable';
|
|
3
3
|
import * as uuid from 'uuid';
|
|
4
4
|
import { CredentialStatusResolverRegistry } from './status/resolver';
|
|
@@ -50,6 +50,7 @@ export class CredentialWallet {
|
|
|
50
50
|
cr['@context'] = context;
|
|
51
51
|
cr.type = credentialType;
|
|
52
52
|
cr.expirationDate = expirationDate ? new Date(expirationDate * 1000).toISOString() : undefined;
|
|
53
|
+
cr.refreshService = request.refreshService;
|
|
53
54
|
cr.issuanceDate = new Date().toISOString();
|
|
54
55
|
cr.credentialSubject = credentialSubject;
|
|
55
56
|
cr.issuer = issuer.string();
|
|
@@ -57,7 +58,7 @@ export class CredentialWallet {
|
|
|
57
58
|
id: request.credentialSchema,
|
|
58
59
|
type: VerifiableConstants.JSON_SCHEMA_VALIDATOR
|
|
59
60
|
};
|
|
60
|
-
cr.credentialStatus = this.buildCredentialStatus(request);
|
|
61
|
+
cr.credentialStatus = this.buildCredentialStatus(request, issuer);
|
|
61
62
|
return cr;
|
|
62
63
|
};
|
|
63
64
|
// if no credential status resolvers are provided
|
|
@@ -123,7 +124,7 @@ export class CredentialWallet {
|
|
|
123
124
|
* @param {CredentialRequest} request
|
|
124
125
|
* @returns `CredentialStatus`
|
|
125
126
|
*/
|
|
126
|
-
buildCredentialStatus(request) {
|
|
127
|
+
buildCredentialStatus(request, issuer) {
|
|
127
128
|
const credentialStatus = {
|
|
128
129
|
id: request.revocationOpts.id,
|
|
129
130
|
type: request.revocationOpts.type,
|
|
@@ -142,6 +143,23 @@ export class CredentialWallet {
|
|
|
142
143
|
? `${credentialStatus.id.replace(/\/$/, '')}/node?state=${request.revocationOpts.issuerState}`
|
|
143
144
|
: `${credentialStatus.id.replace(/\/$/, '')}`
|
|
144
145
|
};
|
|
146
|
+
case CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023: {
|
|
147
|
+
const issuerId = DID.idFromDID(issuer);
|
|
148
|
+
const chainId = getChainId(DID.blockchainFromId(issuerId), DID.networkIdFromId(issuerId));
|
|
149
|
+
const searchParams = [
|
|
150
|
+
['revocationNonce', request.revocationOpts.nonce?.toString() || ''],
|
|
151
|
+
['contractAddress', `${chainId}:${request.revocationOpts.id}`],
|
|
152
|
+
['state', request.revocationOpts.issuerState || '']
|
|
153
|
+
]
|
|
154
|
+
.filter(([, value]) => Boolean(value))
|
|
155
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
156
|
+
.join('&');
|
|
157
|
+
return {
|
|
158
|
+
...credentialStatus,
|
|
159
|
+
// `[did]:[methodid]:[chain]:[network]:[id]/credentialStatus?(revocationNonce=value)&[contractAddress=[chainID]:[contractAddress]]&(state=issuerState)`
|
|
160
|
+
id: `${issuer.string()}/credentialStatus?${searchParams}`
|
|
161
|
+
};
|
|
162
|
+
}
|
|
145
163
|
default:
|
|
146
164
|
return credentialStatus;
|
|
147
165
|
}
|
|
@@ -3,6 +3,7 @@ export * from './status/reverse-sparse-merkle-tree';
|
|
|
3
3
|
export * from './status/sparse-merkle-tree';
|
|
4
4
|
export * from './status/resolver';
|
|
5
5
|
export * from './status/agent-revocation';
|
|
6
|
+
export * from './status/credential-status-publisher';
|
|
6
7
|
export * from './status/utils';
|
|
7
8
|
export * from './credential-wallet';
|
|
8
9
|
export * from './rhs';
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Hash, testBit, NodeLeaf } from '@iden3/js-merkletree';
|
|
2
2
|
import { NODE_TYPE_LEAF } from '@iden3/js-merkletree';
|
|
3
3
|
import { hashElems } from '@iden3/js-merkletree';
|
|
4
4
|
import { ProofNode } from './status/reverse-sparse-merkle-tree';
|
|
5
|
+
import { Iden3SmtRhsCredentialStatusPublisher } from './status/credential-status-publisher';
|
|
6
|
+
import { CredentialStatusType } from '../verifiable';
|
|
5
7
|
/**
|
|
6
8
|
* Pushes identity state information to a reverse hash service.
|
|
7
9
|
*
|
|
8
10
|
* A reverse hash service (RHS) is a centralized or decentralized service for storing publicly available data about identity.
|
|
9
11
|
* Such data are identity state and state of revocation tree and roots tree root tree.
|
|
10
12
|
*
|
|
13
|
+
* @deprecated Use `pushHashesToReverseHashService` instead.
|
|
11
14
|
* @param {Hash} state - current state of identity
|
|
12
15
|
* @param {TreesModel} trees - current trees of identity (claims, revocation, rootOfRoots )
|
|
13
16
|
* @param {string} rhsUrl - URL of service
|
|
@@ -15,6 +18,23 @@ import { ProofNode } from './status/reverse-sparse-merkle-tree';
|
|
|
15
18
|
* @returns void
|
|
16
19
|
*/
|
|
17
20
|
export async function pushHashesToRHS(state, trees, rhsUrl, revokedNonces) {
|
|
21
|
+
const nodes = await getNodesRepresentation(revokedNonces, trees, state);
|
|
22
|
+
const publisher = new Iden3SmtRhsCredentialStatusPublisher();
|
|
23
|
+
await publisher.publish({
|
|
24
|
+
nodes,
|
|
25
|
+
credentialStatusType: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof,
|
|
26
|
+
rhsUrl: rhsUrl
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Retrieves the representation of nodes for generating a proof.
|
|
31
|
+
*
|
|
32
|
+
* @param revokedNonces - An array of revoked nonces.
|
|
33
|
+
* @param trees - The TreesModel object containing the necessary trees.
|
|
34
|
+
* @param state - The hash of the state.
|
|
35
|
+
* @returns A Promise that resolves to an array of ProofNode objects.
|
|
36
|
+
*/
|
|
37
|
+
export async function getNodesRepresentation(revokedNonces, trees, state) {
|
|
18
38
|
const nb = new NodesBuilder();
|
|
19
39
|
if (revokedNonces) {
|
|
20
40
|
await addRevocationNode(nb, trees, revokedNonces);
|
|
@@ -28,15 +48,7 @@ export async function pushHashesToRHS(state, trees, rhsUrl, revokedNonces) {
|
|
|
28
48
|
await trees.rootsTree.root()
|
|
29
49
|
]));
|
|
30
50
|
}
|
|
31
|
-
|
|
32
|
-
await saveNodes(nb.nodes, rhsUrl);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
async function saveNodes(nodes, nodeUrl) {
|
|
36
|
-
const nodesJSON = nodes.map((n) => n.toJSON());
|
|
37
|
-
const resp = await fetch(nodeUrl + '/node', { method: 'post', body: JSON.stringify(nodesJSON) });
|
|
38
|
-
const status = resp.status;
|
|
39
|
-
return status === 200;
|
|
51
|
+
return nb.nodes;
|
|
40
52
|
}
|
|
41
53
|
async function addRoRNode(nb, trees) {
|
|
42
54
|
const currentRootsTree = trees.rootsTree;
|
|
@@ -62,8 +74,8 @@ class NodesBuilder {
|
|
|
62
74
|
}
|
|
63
75
|
async addKey(tree, nodeKey) {
|
|
64
76
|
const { value: nodeValue, siblings } = await tree.get(nodeKey);
|
|
65
|
-
const nodeKeyHash =
|
|
66
|
-
const nodeValueHash =
|
|
77
|
+
const nodeKeyHash = Hash.fromBigInt(nodeKey);
|
|
78
|
+
const nodeValueHash = Hash.fromBigInt(nodeValue);
|
|
67
79
|
const node = new NodeLeaf(nodeKeyHash, nodeValueHash);
|
|
68
80
|
const newNodes = await buildNodesUp(siblings, node);
|
|
69
81
|
for (const n of newNodes) {
|
|
@@ -93,7 +105,7 @@ async function buildNodesUp(siblings, node) {
|
|
|
93
105
|
nodes[index] = new ProofNode();
|
|
94
106
|
}
|
|
95
107
|
nodes[sl].hash = prevHash;
|
|
96
|
-
const hashOfOne =
|
|
108
|
+
const hashOfOne = Hash.fromBigInt(BigInt(1));
|
|
97
109
|
nodes[sl].children = [node.entry[0], node.entry[1], hashOfOne];
|
|
98
110
|
const pathKey = node.entry[0];
|
|
99
111
|
for (let i = sl - 1; i >= 0; i--) {
|
|
@@ -107,7 +119,7 @@ async function buildNodesUp(siblings, node) {
|
|
|
107
119
|
nodes[i].children[0] = prevHash;
|
|
108
120
|
nodes[i].children[1] = siblings[i];
|
|
109
121
|
}
|
|
110
|
-
nodes[i].hash =
|
|
122
|
+
nodes[i].hash = hashElems([nodes[i].children[0].bigInt(), nodes[i].children[1].bigInt()]);
|
|
111
123
|
prevHash = nodes[i].hash;
|
|
112
124
|
}
|
|
113
125
|
return nodes;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { CredentialStatusType } from '../../verifiable';
|
|
2
|
+
/**
|
|
3
|
+
* Registry for managing credential status publishers.
|
|
4
|
+
*/
|
|
5
|
+
export class CredentialStatusPublisherRegistry {
|
|
6
|
+
constructor() {
|
|
7
|
+
this._publishers = new Map();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Registers one or more credential status publishers for a given type.
|
|
11
|
+
* @param type - The credential status type.
|
|
12
|
+
* @param publisher - One or more credential status publishers.
|
|
13
|
+
*/
|
|
14
|
+
register(type, ...publisher) {
|
|
15
|
+
const publishers = this._publishers.get(type) ?? [];
|
|
16
|
+
publishers.push(...publisher);
|
|
17
|
+
this._publishers.set(type, publishers);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Retrieves the credential status publishers for a given type.
|
|
21
|
+
* @param type - The credential status type.
|
|
22
|
+
* @returns An array of credential status publishers or undefined if none are registered for the given type.
|
|
23
|
+
*/
|
|
24
|
+
get(type) {
|
|
25
|
+
return this._publishers.get(type);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Implementation of the ICredentialStatusPublisher interface for publishing on-chain credential status.
|
|
30
|
+
*/
|
|
31
|
+
export class Iden3OnchainSmtCredentialStatusPublisher {
|
|
32
|
+
constructor(_storage) {
|
|
33
|
+
this._storage = _storage;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Publishes the credential status to the blockchain.
|
|
37
|
+
* @param params - The parameters for publishing the credential status.
|
|
38
|
+
*/
|
|
39
|
+
async publish(params) {
|
|
40
|
+
if (![CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023].includes(params.credentialStatusType)) {
|
|
41
|
+
throw new Error(`On-chain publishing is not supported for credential status type ${params.credentialStatusType}`);
|
|
42
|
+
}
|
|
43
|
+
const nodesBigInts = params.nodes.map((n) => n.children.map((c) => c.bigInt()));
|
|
44
|
+
const txPromise = this._storage.saveNodes(nodesBigInts);
|
|
45
|
+
if (params.onChain?.txCallback) {
|
|
46
|
+
const cb = params.onChain?.txCallback;
|
|
47
|
+
txPromise.then((receipt) => cb(receipt));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
await txPromise;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Implementation of the ICredentialStatusPublisher interface for publishing off-chain credential status.
|
|
55
|
+
*/
|
|
56
|
+
export class Iden3SmtRhsCredentialStatusPublisher {
|
|
57
|
+
/**
|
|
58
|
+
* Publishes the credential status to a specified node URL.
|
|
59
|
+
* @param params - The parameters for publishing the credential status.
|
|
60
|
+
* @param params.nodes - The proof nodes to be published.
|
|
61
|
+
* @param params.rhsUrl - The URL of the node to publish the credential status to.
|
|
62
|
+
* @returns A promise that resolves when the credential status is successfully published.
|
|
63
|
+
* @throws An error if the publishing fails.
|
|
64
|
+
*/
|
|
65
|
+
async publish(params) {
|
|
66
|
+
if (![CredentialStatusType.Iden3ReverseSparseMerkleTreeProof].includes(params.credentialStatusType)) {
|
|
67
|
+
throw new Error(`On-chain publishing is not supported for credential status type ${params.credentialStatusType}`);
|
|
68
|
+
}
|
|
69
|
+
const nodesJSON = params.nodes.map((n) => n.toJSON());
|
|
70
|
+
const resp = await fetch(params.rhsUrl + '/node', {
|
|
71
|
+
method: 'post',
|
|
72
|
+
body: JSON.stringify(nodesJSON)
|
|
73
|
+
});
|
|
74
|
+
if (resp.status !== 200) {
|
|
75
|
+
throw new Error(`Failed to publish credential status. Status: ${resp.status}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { DID } from '@iden3/js-iden3-core';
|
|
2
|
+
import { PublicKey } from '@iden3/js-crypto';
|
|
3
|
+
import { KmsKeyType, keyPath } from '../kms';
|
|
2
4
|
/**
|
|
3
5
|
* Retrieves the user DID from a given credential.
|
|
4
6
|
* If the credential does not have a credentialSubject.id property, the issuer DID is returned.
|
|
@@ -17,3 +19,13 @@ export const getUserDIDFromCredential = (issuerDID, credential) => {
|
|
|
17
19
|
}
|
|
18
20
|
return DID.parse(credential.credentialSubject.id);
|
|
19
21
|
};
|
|
22
|
+
export const getKMSIdByAuthCredential = (credential) => {
|
|
23
|
+
if (!credential.type.includes('AuthBJJCredential')) {
|
|
24
|
+
throw new Error("can't sign with not AuthBJJCredential credential");
|
|
25
|
+
}
|
|
26
|
+
const x = credential.credentialSubject['x'];
|
|
27
|
+
const y = credential.credentialSubject['y'];
|
|
28
|
+
const pb = new PublicKey([BigInt(x), BigInt(y)]);
|
|
29
|
+
const kp = keyPath(KmsKeyType.BabyJubJub, pb.hex());
|
|
30
|
+
return { type: KmsKeyType.BabyJubJub, id: kp };
|
|
31
|
+
};
|
|
@@ -15,6 +15,8 @@ export const PROTOCOL_MESSAGE_TYPE = Object.freeze({
|
|
|
15
15
|
CREDENTIAL_OFFER_MESSAGE_TYPE: IDEN3_PROTOCOL + 'credentials/1.0/offer',
|
|
16
16
|
// CredentialIssuanceResponseMessageType is type for message with a credential issuance
|
|
17
17
|
CREDENTIAL_ISSUANCE_RESPONSE_MESSAGE_TYPE: IDEN3_PROTOCOL + 'credentials/1.0/issuance-response',
|
|
18
|
+
// CredentialRefreshMessageType is type for message with a credential issuance
|
|
19
|
+
CREDENTIAL_REFRESH_MESSAGE_TYPE: IDEN3_PROTOCOL + 'credentials/1.0/refresh',
|
|
18
20
|
// DeviceRegistrationRequestMessageType defines device registration request type of the communication protocol
|
|
19
21
|
DEVICE_REGISTRATION_REQUEST_MESSAGE_TYPE: IDEN3_PROTOCOL + 'devices/1.0/registration',
|
|
20
22
|
// MessageFetMessageFetchRequestMessageType defines message fetch request type of the communication protocol.
|
|
@@ -16,7 +16,7 @@ import { DID } from '@iden3/js-iden3-core';
|
|
|
16
16
|
*/
|
|
17
17
|
export class FetchHandler {
|
|
18
18
|
/**
|
|
19
|
-
* Creates an instance of
|
|
19
|
+
* Creates an instance of FetchHandler.
|
|
20
20
|
* @param {IPackageManager} _packerMgr - package manager to unpack message envelope
|
|
21
21
|
*/
|
|
22
22
|
constructor(_packerMgr) {
|
|
@@ -29,7 +29,7 @@ export class FetchHandler {
|
|
|
29
29
|
* offer: Uint8Array; offer - raw offer message
|
|
30
30
|
* opts
|
|
31
31
|
* }) options how to fetch credential
|
|
32
|
-
* @returns `Promise<W3CCredential>`
|
|
32
|
+
* @returns `Promise<W3CCredential[]>`
|
|
33
33
|
*/
|
|
34
34
|
async handleCredentialOffer(offer, opts) {
|
|
35
35
|
if (!opts) {
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { MediaType } from '../constants';
|
|
2
|
+
import { PROTOCOL_MESSAGE_TYPE } from '../constants';
|
|
3
|
+
import { RefreshServiceType, W3CCredential } from '../../verifiable';
|
|
4
|
+
import { byteEncoder } from '../../utils';
|
|
5
|
+
import { proving, ProvingMethodAlg } from '@iden3/js-jwz';
|
|
6
|
+
import { DID } from '@iden3/js-iden3-core';
|
|
7
|
+
import { CircuitId } from '../../circuits';
|
|
8
|
+
import { randomUUID } from 'crypto';
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* Allows to refresh credential from refresh service and return refreshed credential
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
|
|
15
|
+
* @class RefreshHandler
|
|
16
|
+
* @implements implements RefreshHandler interface
|
|
17
|
+
*/
|
|
18
|
+
export class RefreshHandler {
|
|
19
|
+
/**
|
|
20
|
+
* Creates an instance of RefreshHandler.
|
|
21
|
+
* @param {RefreshHandlerOptions} _options - refresh handler options
|
|
22
|
+
*/
|
|
23
|
+
constructor(_options) {
|
|
24
|
+
this._options = _options;
|
|
25
|
+
}
|
|
26
|
+
async refreshCredential(credential, opts) {
|
|
27
|
+
if (!credential.refreshService) {
|
|
28
|
+
throw new Error('refreshService not specified for W3CCredential');
|
|
29
|
+
}
|
|
30
|
+
if (credential.refreshService.type !== RefreshServiceType.Iden3RefreshService2023) {
|
|
31
|
+
throw new Error(`refresh service type ${credential.refreshService.type} is not supported`);
|
|
32
|
+
}
|
|
33
|
+
const otherIdentifier = credential.credentialSubject.id;
|
|
34
|
+
if (!otherIdentifier) {
|
|
35
|
+
throw new Error('self credentials do not support refresh');
|
|
36
|
+
}
|
|
37
|
+
const senderDID = DID.parse(otherIdentifier);
|
|
38
|
+
const zkpParams = {
|
|
39
|
+
senderDID,
|
|
40
|
+
provingMethodAlg: new ProvingMethodAlg(proving.provingMethodGroth16AuthV2Instance.methodAlg.alg, CircuitId.AuthV2)
|
|
41
|
+
};
|
|
42
|
+
const refreshMsg = {
|
|
43
|
+
id: randomUUID(),
|
|
44
|
+
typ: MediaType.ZKPMessage,
|
|
45
|
+
type: PROTOCOL_MESSAGE_TYPE.CREDENTIAL_REFRESH_MESSAGE_TYPE,
|
|
46
|
+
thid: randomUUID(),
|
|
47
|
+
body: {
|
|
48
|
+
id: otherIdentifier,
|
|
49
|
+
reason: opts?.reason ?? 'credential is expired'
|
|
50
|
+
},
|
|
51
|
+
from: otherIdentifier,
|
|
52
|
+
to: credential.issuer
|
|
53
|
+
};
|
|
54
|
+
const msgBytes = byteEncoder.encode(JSON.stringify(refreshMsg));
|
|
55
|
+
const jwzToken = await this._options.packageManager.pack(MediaType.ZKPMessage, msgBytes, zkpParams);
|
|
56
|
+
const resp = await fetch(credential.refreshService.id, {
|
|
57
|
+
method: 'post',
|
|
58
|
+
headers: {
|
|
59
|
+
'Content-Type': 'application/json'
|
|
60
|
+
},
|
|
61
|
+
body: jwzToken
|
|
62
|
+
});
|
|
63
|
+
if (resp.status !== 200) {
|
|
64
|
+
throw new Error(`could not refresh W3C credential, return status ${resp.status}`);
|
|
65
|
+
}
|
|
66
|
+
const respBody = await resp.json();
|
|
67
|
+
if (!respBody.body?.credential) {
|
|
68
|
+
throw new Error('no credential in CredentialIssuanceMessage response');
|
|
69
|
+
}
|
|
70
|
+
return W3CCredential.fromJSON(respBody.body.credential);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -5,11 +5,11 @@ import { hashElems, ZERO_HASH } from '@iden3/js-merkletree';
|
|
|
5
5
|
import { generateProfileDID, subjectPositionIndex } from './common';
|
|
6
6
|
import * as uuid from 'uuid';
|
|
7
7
|
import { Parser, JsonSchemaValidator, cacheLoader } from '../schema-processor';
|
|
8
|
-
import { MerkleTreeType } from '../storage
|
|
9
|
-
import { keyPath } from '../kms/provider-helpers';
|
|
8
|
+
import { MerkleTreeType } from '../storage';
|
|
10
9
|
import { VerifiableConstants, BJJSignatureProof2021, MerklizedRootPosition, SubjectPosition, W3CCredential, Iden3SparseMerkleTreeProof, ProofType, CredentialStatusType } from '../verifiable';
|
|
11
|
-
import { pushHashesToRHS } from '../credentials';
|
|
10
|
+
import { getKMSIdByAuthCredential, getNodesRepresentation, pushHashesToRHS } from '../credentials';
|
|
12
11
|
import { byteEncoder } from '../utils';
|
|
12
|
+
import { CredentialStatusPublisherRegistry, Iden3SmtRhsCredentialStatusPublisher } from '../credentials/status/credential-status-publisher';
|
|
13
13
|
/**
|
|
14
14
|
* @public
|
|
15
15
|
* Wallet instance to manage the digital identity based on iden3 protocol
|
|
@@ -30,10 +30,19 @@ export class IdentityWallet {
|
|
|
30
30
|
* @param {ICredentialWallet} _credentialWallet - credential wallet instance to quickly access credential CRUD functionality
|
|
31
31
|
* @public
|
|
32
32
|
*/
|
|
33
|
-
constructor(_kms, _storage, _credentialWallet) {
|
|
33
|
+
constructor(_kms, _storage, _credentialWallet, _opts) {
|
|
34
34
|
this._kms = _kms;
|
|
35
35
|
this._storage = _storage;
|
|
36
36
|
this._credentialWallet = _credentialWallet;
|
|
37
|
+
this._opts = _opts;
|
|
38
|
+
if (!_opts?.credentialStatusPublisherRegistry) {
|
|
39
|
+
this._credentialStatusPublisherRegistry = new CredentialStatusPublisherRegistry();
|
|
40
|
+
this._credentialStatusPublisherRegistry.register(CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, new Iden3SmtRhsCredentialStatusPublisher());
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
this._credentialStatusPublisherRegistry = this._opts
|
|
44
|
+
?.credentialStatusPublisherRegistry;
|
|
45
|
+
}
|
|
37
46
|
}
|
|
38
47
|
/**
|
|
39
48
|
* {@inheritDoc IIdentityWallet.createIdentity}
|
|
@@ -107,9 +116,10 @@ export class IdentityWallet {
|
|
|
107
116
|
coreClaim: authClaim
|
|
108
117
|
});
|
|
109
118
|
credential.proof = [mtpProof];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
await this.publishRevocationInfoByCredentialStatusType(did, opts.revocationOpts.type, {
|
|
120
|
+
rhsUrl: opts.revocationOpts.id,
|
|
121
|
+
onChain: opts.revocationOpts.onChain
|
|
122
|
+
});
|
|
113
123
|
await this._storage.identity.saveIdentity({
|
|
114
124
|
did: did.string(),
|
|
115
125
|
state: currentState,
|
|
@@ -171,10 +181,11 @@ export class IdentityWallet {
|
|
|
171
181
|
}
|
|
172
182
|
/** {@inheritDoc IIdentityWallet.getDIDTreeModel} */
|
|
173
183
|
async getDIDTreeModel(did) {
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
const
|
|
184
|
+
const didStr = did.string();
|
|
185
|
+
const claimsTree = await this._storage.mt.getMerkleTreeByIdentifierAndType(didStr, MerkleTreeType.Claims);
|
|
186
|
+
const revocationTree = await this._storage.mt.getMerkleTreeByIdentifierAndType(didStr, MerkleTreeType.Revocations);
|
|
187
|
+
const rootsTree = await this._storage.mt.getMerkleTreeByIdentifierAndType(didStr, MerkleTreeType.Roots);
|
|
188
|
+
const state = hashElems([
|
|
178
189
|
(await claimsTree.root()).bigInt(),
|
|
179
190
|
(await revocationTree.root()).bigInt(),
|
|
180
191
|
(await rootsTree.root()).bigInt()
|
|
@@ -228,14 +239,14 @@ export class IdentityWallet {
|
|
|
228
239
|
}
|
|
229
240
|
/** {@inheritDoc IIdentityWallet.sign} */
|
|
230
241
|
async sign(message, credential) {
|
|
231
|
-
const keyKMSId =
|
|
242
|
+
const keyKMSId = getKMSIdByAuthCredential(credential);
|
|
232
243
|
const payload = poseidon.hashBytes(message);
|
|
233
244
|
const signature = await this._kms.sign(keyKMSId, BytesHelper.intToBytes(payload));
|
|
234
245
|
return Signature.newFromCompressed(signature);
|
|
235
246
|
}
|
|
236
247
|
/** {@inheritDoc IIdentityWallet.signChallenge} */
|
|
237
248
|
async signChallenge(challenge, credential) {
|
|
238
|
-
const keyKMSId =
|
|
249
|
+
const keyKMSId = getKMSIdByAuthCredential(credential);
|
|
239
250
|
const signature = await this._kms.sign(keyKMSId, BytesHelper.intToBytes(challenge));
|
|
240
251
|
return Signature.newFromCompressed(signature);
|
|
241
252
|
}
|
|
@@ -403,15 +414,35 @@ export class IdentityWallet {
|
|
|
403
414
|
rootsTree: treeState.rootsTree
|
|
404
415
|
}, rhsURL, revokedNonces);
|
|
405
416
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
417
|
+
/** {@inheritDoc IIdentityWallet.publishRevocationInfoByCredentialStatusType} */
|
|
418
|
+
async publishRevocationInfoByCredentialStatusType(issuerDID, credentialStatusType, opts) {
|
|
419
|
+
const rhsPublishers = this._credentialStatusPublisherRegistry.get(credentialStatusType);
|
|
420
|
+
if (!rhsPublishers) {
|
|
421
|
+
throw new Error(`there is no registered publisher to save hash is not registered for ${credentialStatusType} is not registered`);
|
|
422
|
+
}
|
|
423
|
+
let nodes = [];
|
|
424
|
+
if (opts?.treeModel) {
|
|
425
|
+
nodes = await getNodesRepresentation(opts.revokedNonces, {
|
|
426
|
+
revocationTree: opts.treeModel.revocationTree,
|
|
427
|
+
claimsTree: opts.treeModel.claimsTree,
|
|
428
|
+
state: opts.treeModel.state,
|
|
429
|
+
rootsTree: opts.treeModel.rootsTree
|
|
430
|
+
}, opts.treeModel.state);
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
const treeState = await this.getDIDTreeModel(issuerDID);
|
|
434
|
+
nodes = await getNodesRepresentation(opts?.revokedNonces, {
|
|
435
|
+
revocationTree: treeState.revocationTree,
|
|
436
|
+
claimsTree: treeState.claimsTree,
|
|
437
|
+
state: treeState.state,
|
|
438
|
+
rootsTree: treeState.rootsTree
|
|
439
|
+
}, treeState.state);
|
|
440
|
+
}
|
|
441
|
+
if (!nodes.length) {
|
|
442
|
+
return;
|
|
409
443
|
}
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
const pb = new PublicKey([BigInt(x), BigInt(y)]);
|
|
413
|
-
const kp = keyPath(KmsKeyType.BabyJubJub, pb.hex());
|
|
414
|
-
return { type: KmsKeyType.BabyJubJub, id: kp };
|
|
444
|
+
const rhsPublishersTask = rhsPublishers.map((publisher) => publisher.publish({ nodes, ...opts, credentialStatusType, issuerDID }));
|
|
445
|
+
await Promise.all(rhsPublishersTask);
|
|
415
446
|
}
|
|
416
447
|
async getCoreClaimFromCredential(credential) {
|
|
417
448
|
const coreClaimFromSigProof = credential.getCoreClaimFromProof(ProofType.BJJSignature);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
[{ "inputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }, { "internalType": "uint64", "name": "nonce", "type": "uint64" }], "name": "getRevocationStatus", "outputs": [{ "components": [{ "components": [{ "internalType": "uint256", "name": "state", "type": "uint256" }, { "internalType": "uint256", "name": "claimsTreeRoot", "type": "uint256" }, { "internalType": "uint256", "name": "revocationTreeRoot", "type": "uint256" }, { "internalType": "uint256", "name": "rootOfRoots", "type": "uint256" }], "internalType": "
|
|
1
|
+
[{ "inputs": [], "name": "MAX_SMT_DEPTH", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "key", "type": "uint256" }], "name": "getNode", "outputs": [{ "internalType": "uint256[]", "name": "", "type": "uint256[]" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }, { "internalType": "uint64", "name": "nonce", "type": "uint64" }], "name": "getRevocationStatus", "outputs": [{ "components": [{ "components": [{ "internalType": "uint256", "name": "state", "type": "uint256" }, { "internalType": "uint256", "name": "claimsTreeRoot", "type": "uint256" }, { "internalType": "uint256", "name": "revocationTreeRoot", "type": "uint256" }, { "internalType": "uint256", "name": "rootOfRoots", "type": "uint256" }], "internalType": "struct IOnchainCredentialStatusResolver.IdentityStateRoots", "name": "issuer", "type": "tuple" }, { "components": [{ "internalType": "uint256", "name": "root", "type": "uint256" }, { "internalType": "bool", "name": "existence", "type": "bool" }, { "internalType": "uint256[]", "name": "siblings", "type": "uint256[]" }, { "internalType": "uint256", "name": "index", "type": "uint256" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "bool", "name": "auxExistence", "type": "bool" }, { "internalType": "uint256", "name": "auxIndex", "type": "uint256" }, { "internalType": "uint256", "name": "auxValue", "type": "uint256" }], "internalType": "struct IOnchainCredentialStatusResolver.Proof", "name": "mtp", "type": "tuple" }], "internalType": "struct IOnchainCredentialStatusResolver.CredentialStatus", "name": "", "type": "tuple" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }, { "internalType": "uint256", "name": "state", "type": "uint256" }, { "internalType": "uint64", "name": "nonce", "type": "uint64" }], "name": "getRevocationStatusByIdAndState", "outputs": [{ "components": [{ "components": [{ "internalType": "uint256", "name": "state", "type": "uint256" }, { "internalType": "uint256", "name": "claimsTreeRoot", "type": "uint256" }, { "internalType": "uint256", "name": "revocationTreeRoot", "type": "uint256" }, { "internalType": "uint256", "name": "rootOfRoots", "type": "uint256" }], "internalType": "struct IOnchainCredentialStatusResolver.IdentityStateRoots", "name": "issuer", "type": "tuple" }, { "components": [{ "internalType": "uint256", "name": "root", "type": "uint256" }, { "internalType": "bool", "name": "existence", "type": "bool" }, { "internalType": "uint256[]", "name": "siblings", "type": "uint256[]" }, { "internalType": "uint256", "name": "index", "type": "uint256" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "bool", "name": "auxExistence", "type": "bool" }, { "internalType": "uint256", "name": "auxIndex", "type": "uint256" }, { "internalType": "uint256", "name": "auxValue", "type": "uint256" }], "internalType": "struct IOnchainCredentialStatusResolver.Proof", "name": "mtp", "type": "tuple" }], "internalType": "struct IOnchainCredentialStatusResolver.CredentialStatus", "name": "", "type": "tuple" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "owner", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint256[][]", "name": "nodes", "type": "uint256[][]" }], "name": "saveNodes", "outputs": [], "stateMutability": "nonpayable", "type": "function" }]
|