dkg.js 6.0.0-beta.2.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,377 @@
1
+ const axios = require("axios");
2
+ const FormData = require("form-data");
3
+
4
+ const Logger = require("../utilities/logger");
5
+
6
+ class AbstractClient {
7
+ defaultMaxNumberOfRetries = 50;
8
+ defaultTimeoutInSeconds = 10;
9
+ defaultNumberOfResults = 20;
10
+ STATUSES = {
11
+ pending: "PENDING",
12
+ completed: "COMPLETED",
13
+ failed: "FAILED",
14
+ };
15
+ /**
16
+ * Initialize client
17
+ * @constructor
18
+ * @param {object} options
19
+ * @param {string} options.endpoint
20
+ * @param {number} options.port
21
+ * @param {boolean} options.useSSL
22
+ * @param {string} options.loglevel (optional)
23
+ * @param {number} options.maxNumberOfRetries (optional)
24
+ */
25
+ constructor(options) {
26
+ let loglevel = options.loglevel ? options.loglevel : "error";
27
+ this.maxNumberOfRetries =
28
+ options.maxNumberOfRetries && options.maxNumberOfRetries >= 0
29
+ ? options.maxNumberOfRetries
30
+ : this.defaultMaxNumberOfRetries;
31
+ this.logger = new Logger(loglevel);
32
+ if (!options.endpoint || !options.port) {
33
+ throw Error("Endpoint and port are required parameters");
34
+ }
35
+ this.nodeBaseUrl = `${options.useSSL ? "https://" : "http://"}${
36
+ options.endpoint
37
+ }:${options.port}`;
38
+ this._sendNodeInfoRequest()
39
+ .then()
40
+ .catch((error) => {
41
+ throw new Error(`Endpoint not available: ${error}`);
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Get node information (version, is auto upgrade enabled, is telemetry enabled)
47
+ */
48
+ nodeInfo() {
49
+ return new Promise((resolve, reject) => {
50
+ this._sendNodeInfoRequest()
51
+ .then((response) => resolve(response.data))
52
+ .catch((error) => reject(error));
53
+ });
54
+ }
55
+
56
+ _sendNodeInfoRequest() {
57
+ this.logger.debug("Sending node info request");
58
+ return axios.get(`${this.nodeBaseUrl}/info`, {
59
+ timeout: this.defaultTimeoutInSeconds * 1000,
60
+ });
61
+ }
62
+
63
+ _publishRequest(options) {
64
+ this.logger.debug("Sending publish request.");
65
+ const form = new FormData();
66
+ form.append("data", JSON.stringify(options.content));
67
+ form.append("keywords", JSON.stringify(options.keywords));
68
+ if (options.ual) {
69
+ form.append("ual", options.ual);
70
+ }
71
+ form.append("visibility", options.visibility);
72
+ let axios_config = {
73
+ method: "post",
74
+ url: `${this.nodeBaseUrl}/${options.method}`,
75
+ data: form,
76
+ };
77
+
78
+ return axios(axios_config);
79
+ }
80
+
81
+ /**
82
+ * @param {object} options
83
+ * @param {string[]} options.ids - assertion ids
84
+ */
85
+ resolve(options) {
86
+ if (!options || !options.ids) {
87
+ throw Error("Please provide resolve options in order to resolve.");
88
+ }
89
+ return new Promise((resolve, reject) => {
90
+ this._resolveRequest(options)
91
+ .then((response) =>
92
+ this._getResult({
93
+ handler_id: response.data.handler_id,
94
+ operation: "resolve",
95
+ })
96
+ )
97
+ .then((response) => {
98
+ resolve(response);
99
+ })
100
+ .catch((error) => reject(error));
101
+ });
102
+ }
103
+
104
+ _resolveRequest(options) {
105
+ this.logger.debug("Sending resolve request.");
106
+ const form = new FormData();
107
+ let ids = "";
108
+
109
+ let firstOne = true;
110
+ for (let id of options.ids) {
111
+ firstOne = false;
112
+ if (firstOne) {
113
+ ids += `ids=${id}`;
114
+ } else {
115
+ ids += `&ids=${id}`;
116
+ }
117
+ }
118
+
119
+ let axios_config = {
120
+ method: "get",
121
+ url: `${this.nodeBaseUrl}/resolve?${ids}`,
122
+ data: form,
123
+ };
124
+ return axios(axios_config);
125
+ }
126
+
127
+ /**
128
+ * @param {object} options
129
+ * @param {string} options.query - search term
130
+ * @param {string} options.resultType - result type: assertions or entities
131
+ * @param {boolean} options.prefix (optional)
132
+ * @param {number} options.limit (optional)
133
+ * @param {string[]} options.issuers (optional)
134
+ * @param {string} options.schemaTypes (optional)
135
+ * @param {number} options.numberOfResults (optional)
136
+ * @param {number} options.timeout (optional)
137
+ */
138
+ search(options) {
139
+ if (!options || !options.query || !options.resultType) {
140
+ throw Error("Please provide search options in order to search.");
141
+ }
142
+ return new Promise((resolve, reject) => {
143
+ this._searchRequest(options)
144
+ .then((response) =>
145
+ this._getSearchResult({
146
+ handler_id: response.data.handler_id,
147
+ resultType: options.resultType,
148
+ numberOfResults: options.numberOfResults,
149
+ timeout: options.timeout,
150
+ })
151
+ )
152
+ .then((response) => {
153
+ resolve(response);
154
+ })
155
+ .catch((error) => reject(error));
156
+ });
157
+ }
158
+
159
+ _searchRequest(options) {
160
+ this.logger.debug("Sending search request.");
161
+ const form = new FormData();
162
+ let prefix = options.prefix ? options.prefix : true;
163
+ let limit = options.limit ? options.limit : 20;
164
+ let query = options.query;
165
+ let resultType = options.resultType;
166
+ let url = `${this.nodeBaseUrl}/${resultType}:search?query=${query}`;
167
+ if (resultType === "entities") {
168
+ url = `${this.nodeBaseUrl}/${resultType}:search?query=${query}&limit=${limit}&prefix=${prefix}`;
169
+ }
170
+ let axios_config = {
171
+ method: "get",
172
+ url,
173
+ data: form,
174
+ };
175
+ return axios(axios_config);
176
+ }
177
+
178
+ async _getSearchResult(options) {
179
+ if (!options.handler_id) {
180
+ throw Error("Unable to get results, need handler id");
181
+ }
182
+ let searchResponse = {};
183
+ let retries = 0;
184
+ let timeout = options.timeout ? options.timeout : this.defaultTimeoutInSeconds;
185
+ let numberOfResults = options.numberOfResults
186
+ ? options.numberOfResults
187
+ : this.defaultNumberOfResults;
188
+
189
+ const form = new FormData();
190
+ let axios_config = {
191
+ method: "get",
192
+ url: `${this.nodeBaseUrl}/${options.resultType}:search/result/${options.handler_id}`,
193
+ };
194
+
195
+ let timeoutFlag = false;
196
+ let currentNumberOfResults = numberOfResults;
197
+ setTimeout(() => {
198
+ timeoutFlag = true;
199
+ }, timeout * 1000);
200
+
201
+ do {
202
+ await this.sleepForMilliseconds(1 * 1000);
203
+ try {
204
+ searchResponse = await axios(axios_config);
205
+ currentNumberOfResults = searchResponse.data.itemListElement.length;
206
+ } catch (e) {
207
+ this.logger.error(e);
208
+ throw e;
209
+ }
210
+ } while (!timeoutFlag && numberOfResults > currentNumberOfResults);
211
+ return searchResponse.data;
212
+ }
213
+
214
+ /**
215
+ * @param {object} options
216
+ * @param {string} options.query - sparql query
217
+ */
218
+ query(options) {
219
+ if (!options || !options.query) {
220
+ throw Error("Please provide options in order to query.");
221
+ }
222
+ return new Promise((resolve, reject) => {
223
+ this._queryRequest(options)
224
+ .then((response) =>
225
+ this._getResult({
226
+ handler_id: response.data.handler_id,
227
+ operation: "query",
228
+ })
229
+ )
230
+ .then((response)=>{
231
+ resolve(response);
232
+ })
233
+ .catch((error) => reject(error));
234
+ });
235
+ }
236
+
237
+ _queryRequest(options) {
238
+ this.logger.debug("Sending query request.");
239
+ const form = new FormData();
240
+ let type = options.type ? options.type : "construct";
241
+ let sparqlQuery = options.query;
242
+ form.append("query", sparqlQuery);
243
+ let axios_config = {
244
+ method: "post",
245
+ url: `${this.nodeBaseUrl}/query?type=${type}`,
246
+ data: form,
247
+ };
248
+ return axios(axios_config);
249
+ }
250
+
251
+ /**
252
+ * @param {object} options
253
+ * @param {string[]} options.nquads
254
+ * @param {object} options.validationInstructions
255
+ */
256
+ validate(options) {
257
+ if (!options || !options.nquads) {
258
+ throw Error(
259
+ "Please provide assertions and nquads in order to get proofs."
260
+ );
261
+ }
262
+ return new Promise((resolve, reject) => {
263
+ this._getProofsRequest(options)
264
+ .then((response) =>
265
+ this._getResult({
266
+ handler_id: response.data.handler_id,
267
+ operation: "proofs:get",
268
+ })
269
+ )
270
+ .then(async (response) => {
271
+ if (response.status === this.STATUSES.completed) {
272
+ response = await this._performValidation(response.data);
273
+ } else {
274
+ throw Error("Unable to get proofs for given nquads");
275
+ }
276
+ resolve(response);
277
+ })
278
+ .catch((error) => reject(error));
279
+ });
280
+ }
281
+
282
+ _getProofsRequest(options) {
283
+ this.logger.debug("Sending get proofs request.");
284
+ const form = new FormData();
285
+ let nquads = options.nquads;
286
+ form.append("nquads", JSON.stringify(nquads));
287
+ let axios_config = {
288
+ method: "post",
289
+ url: `${this.nodeBaseUrl}/proofs:get`,
290
+ data: form,
291
+ };
292
+ return axios(axios_config);
293
+ }
294
+
295
+ async _performValidation(assertions) {
296
+ let validationResult = [];
297
+ for (let assertion of assertions) {
298
+ let rootHash = await this._fetchRootHash(assertion.assertionId);
299
+ for (let obj of assertion.proofs) {
300
+ let validatedTriple = { triple: obj.triple, valid: false };
301
+ if (obj.proof === null) {
302
+ this.logger.debug(
303
+ `${obj.triple} has no proof in assertion ${assertion.assertionId}`
304
+ );
305
+ continue;
306
+ }
307
+ let verified = this._validateProof(obj, rootHash);
308
+ validatedTriple.valid = verified;
309
+ validationResult.push(validatedTriple);
310
+ if (verified) {
311
+ this.logger.debug(
312
+ `Validation successful for data: ${JSON.stringify(obj)}`
313
+ );
314
+ } else {
315
+ this.logger.debug(`Invalid data: ${JSON.stringify(obj)}`);
316
+ }
317
+ }
318
+ }
319
+ return validationResult;
320
+ }
321
+
322
+ async _fetchRootHash(assertionId) {
323
+ let result = await this.resolve({ ids: [assertionId] });
324
+ return result.data[0][assertionId].rootHash;
325
+ }
326
+
327
+ _validateProof(obj, rootHash) {
328
+ // const tree = new MerkleTools();
329
+ // const leaf = obj.tripleHash;
330
+ // const verified = tree.validateProof(obj.proof, leaf, rootHash);
331
+ // return verified;
332
+ }
333
+
334
+ async _getResult(options) {
335
+ await this.sleepForMilliseconds(500);
336
+ if (!options.handler_id || !options.operation) {
337
+ throw Error("Unable to get results, need handler id and operation");
338
+ }
339
+ let response = {
340
+ status: this.STATUSES.pending,
341
+ };
342
+ let retries = 0;
343
+ const form = new FormData();
344
+ let axios_config = {
345
+ method: "get",
346
+ url: `${this.nodeBaseUrl}/${options.operation}/result/${options.handler_id}`,
347
+ };
348
+ do {
349
+ if (retries > this.maxNumberOfRetries) {
350
+ throw Error("Unable to get results. Max number of retries reached.");
351
+ }
352
+ retries++;
353
+ await this.sleepForMilliseconds(1 * 1000);
354
+ try {
355
+ response = await axios(axios_config);
356
+ this.logger.debug(
357
+ `${options.operation} result status: ${response.data.status}`
358
+ );
359
+ } catch (e) {
360
+ this.logger.error(e);
361
+ throw e;
362
+ }
363
+ } while (response.data.status === this.STATUSES.pending);
364
+ if (response.data.status === this.STATUSES.failed) {
365
+ throw Error(
366
+ `Get ${options.operation} failed. Reason: ${response.data.message}.`
367
+ );
368
+ }
369
+ return response.data;
370
+ }
371
+
372
+ async sleepForMilliseconds(milliseconds) {
373
+ await new Promise((r) => setTimeout(r, milliseconds));
374
+ }
375
+ }
376
+
377
+ module.exports = AbstractClient;
@@ -0,0 +1,141 @@
1
+ const AbstractClient = require("./abstract-client");
2
+ const AssetsProxyPath = require("../utilities/assets-proxy-path");
3
+
4
+ class AssetsClient extends AbstractClient {
5
+ constructor(options) {
6
+ super(options);
7
+ this._assetsProxyPath = new AssetsProxyPath(options);
8
+ this.loadMetamask()
9
+ }
10
+
11
+ /**
12
+ * @param content
13
+ * @param {object} options
14
+ * @param {string} options.filepath - path to the dataset
15
+ * @param {string[]} options.keywords (optional)
16
+ */
17
+ async create(content, options) {
18
+ content['@context'] = "https://www.schema.org/";
19
+ content.proof = await this.signMessage(content.toString());
20
+
21
+ options.content = content;
22
+ options.method = 'provision';
23
+ return new Promise((resolve, reject) => {
24
+ this._publishRequest(options)
25
+ .then((response) =>
26
+ this._getResult({
27
+ handler_id: response.data.handler_id,
28
+ operation: options.method,
29
+ })
30
+ )
31
+ .then((response) => {
32
+ resolve(response);
33
+ })
34
+ .catch((error) => reject(error));
35
+ });
36
+ }
37
+
38
+ /**
39
+ * @param {object} options
40
+ * @param {string} options.filepath - path to the dataset
41
+ * @param {string[]} options.keywords (optional)
42
+ */
43
+ async update(content, ual, options) {
44
+ content['@context'] = "https://www.schema.org/";
45
+ content.proof = await this.signMessage(content.toString());
46
+ options.content = content;
47
+ options.ual = ual;
48
+ options.method = 'update';
49
+ return new Promise((resolve, reject) => {
50
+ this._publishRequest(options)
51
+ .then((response) =>
52
+ this._getResult({
53
+ handler_id: response.data.handler_id,
54
+ operation: options.method,
55
+ })
56
+ )
57
+ .then((response) => {
58
+ resolve(response);
59
+ })
60
+ .catch((error) => reject(error));
61
+ });
62
+ }
63
+
64
+ async get(ual, commitHash) {
65
+ //TODO add cache
66
+
67
+ let result;
68
+ if (commitHash)
69
+ result = await this.resolve({ids: [commitHash]});
70
+ else
71
+ result = await this.resolve({ids: [ual]});
72
+ if (result.status === this.STATUSES.completed) {
73
+ const data = result.data[0].result;
74
+
75
+ return this._assetsProxyPath.createPath(
76
+ Object.assign(Object.create(null), undefined, undefined),
77
+ Object.assign(Object.create(null), undefined, data),
78
+ ual
79
+ );
80
+ }
81
+
82
+ return undefined;
83
+ }
84
+
85
+ async getStateCommitHashes(ual) {
86
+ //TODO add cache
87
+
88
+ let result = await this.resolve({ids: [ual]});
89
+ if (result.status === this.STATUSES.completed) {
90
+ return result.data[0].result.assertions;
91
+ }
92
+ return undefined;
93
+ }
94
+
95
+
96
+ transfer(options) {
97
+ //TODO
98
+ }
99
+
100
+ approve(options) {
101
+ //TODO
102
+ }
103
+
104
+
105
+ loadMetamask(){
106
+ if (window.ethereum) {
107
+ window.web3 = new Web3(ethereum);
108
+ ethereum.enable()
109
+ .then(() => {
110
+ console.log("Ethereum enabled");
111
+
112
+ web3.eth.getAccounts(function (err, acc) {
113
+ if (err != null) {
114
+ self.setStatus("There was an error fetching your accounts");
115
+ return;
116
+ }
117
+ if (acc.length > 0) {
118
+ console.log(acc);
119
+ }
120
+ });
121
+ })
122
+ .catch(() => {
123
+ console.warn('User didn\'t allow access to accounts.');
124
+ waitLogin();
125
+ });
126
+ } else {
127
+ console.log("Non-Ethereum browser detected. You should consider installing MetaMask.");
128
+ }
129
+ }
130
+
131
+ async signMessage(message) {
132
+ const web3 = new Web3(window.ethereum);
133
+ var hash = web3.utils.sha3(message)
134
+ var accounts = await web3.eth.getAccounts()
135
+ var signature = await web3.eth.personal.sign(hash, accounts[0])
136
+ return {hash, account: accounts[0], signature}
137
+ }
138
+
139
+ }
140
+
141
+ module.exports = AssetsClient;
@@ -0,0 +1,34 @@
1
+ const AbstractClient = require("./abstract-client");
2
+
3
+ class NativeClient extends AbstractClient {
4
+ constructor(props) {
5
+ super(props);
6
+ }
7
+
8
+ /**
9
+ * @param {object} options
10
+ * @param {string} options.filepath - path to the dataset
11
+ * @param {string[]} options.keywords (optional)
12
+ */
13
+ publish(options) {
14
+ if (!options || !options.filepath) {
15
+ throw Error("Please provide publish options in order to publish.");
16
+ }
17
+ options.method = 'publish';
18
+ return new Promise((resolve, reject) => {
19
+ this._publishRequest(options)
20
+ .then((response) =>
21
+ this._getResult({
22
+ handler_id: response.data.handler_id,
23
+ operation: options.method,
24
+ })
25
+ )
26
+ .then((response) => {
27
+ resolve(response);
28
+ })
29
+ .catch((error) => reject(error));
30
+ });
31
+ }
32
+ }
33
+
34
+ module.exports = NativeClient;
package/dist/.gitkeep ADDED
File without changes