@tdxvolt/volt-client-grpc 0.11.0 → 0.11.3
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/lib/index.cjs +1862 -1806
- package/package.json +2 -2
- package/src/constants.js +72 -72
- package/src/grpc-call.js +267 -266
- package/src/grpc-utils.js +206 -206
- package/src/proto-utils.js +61 -61
- package/src/utils.js +64 -64
- package/src/volt-client-internal.js +308 -308
- package/src/volt-client.js +601 -541
- package/src/volt-connection.js +47 -52
- package/src/volt-credential.js +221 -221
|
@@ -16,344 +16,344 @@ const { filter, pick } = lodash;
|
|
|
16
16
|
|
|
17
17
|
const log = debug("volt-client-grpc:volt-client-internal");
|
|
18
18
|
const voltServices = [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
constants.serviceType.voltAPI,
|
|
20
|
+
constants.serviceType.fileAPI,
|
|
21
|
+
constants.serviceType.sqliteDatabaseAPI,
|
|
22
|
+
constants.serviceType.sqliteServerAPI,
|
|
23
|
+
constants.serviceType.ssiAPI,
|
|
24
|
+
constants.serviceType.relayAPI,
|
|
25
|
+
constants.serviceType.wireAPI,
|
|
26
26
|
];
|
|
27
27
|
|
|
28
28
|
function issueBind(bindRequest, ttl) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
29
|
+
// eslint-disable-next-line no-use-before-define
|
|
30
|
+
return unaryCall.call(this, "Bind", bindRequest).then((bindResponse) => {
|
|
31
|
+
log("got binding request response %j", bindResponse);
|
|
32
|
+
if (bindResponse.status?.code) {
|
|
33
|
+
log("error in bind response: %s", bindResponse.status.message);
|
|
34
|
+
return Promise.reject(new Error(bindResponse.status.message));
|
|
35
|
+
} else {
|
|
36
|
+
switch (bindResponse.decision) {
|
|
37
|
+
case "POLICY_DECISION_PERMIT": {
|
|
38
|
+
//
|
|
39
|
+
// The request status is permit => cache the bind response info.
|
|
40
|
+
//
|
|
41
|
+
|
|
42
|
+
// This is the certificate assigned to us by the volt.
|
|
43
|
+
this._credential.cache.cert = bindResponse.cert;
|
|
44
|
+
|
|
45
|
+
// This is the signing CA used by the volt (don't ov)
|
|
46
|
+
this._credential.cache.ca = bindResponse.chain;
|
|
47
|
+
|
|
48
|
+
// Resource id is assigned by the volt.
|
|
49
|
+
this._credential.cache.client_id = bindResponse.identity_id;
|
|
50
|
+
this._credential.saveCache();
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case "POLICY_DECISION_DENY": {
|
|
54
|
+
log(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case "POLICY_DECISION_PROMPT":
|
|
58
|
+
case "POLICY_DECISION_PENDING": {
|
|
59
|
+
log("access pending approval - waiting...");
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
default:
|
|
63
|
+
log("ignoring unknown bind descision %s", bindResponse.status);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return bindResponse.decision;
|
|
69
|
+
});
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function findDIDDocumentService(document, serviceType) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
if (!document.service) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
const matches = filter(document.service, (svc) => svc.type === serviceType);
|
|
78
|
+
return matches;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
export async function bindInternal() {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
82
|
+
try {
|
|
83
|
+
// Check if we need to resolve a Relay volt.
|
|
84
|
+
if (
|
|
85
|
+
typeof this._voltConfig?.relay === "string" ||
|
|
86
|
+
this._voltConfig?.relay?.http_address
|
|
87
|
+
) {
|
|
88
|
+
const relayURL =
|
|
89
|
+
this._voltConfig.relay?.http_address || this._voltConfig.relay;
|
|
90
|
+
log("attempting to retrieve Relay information from %s", relayURL);
|
|
91
|
+
this._voltConfig.relay = await fetchVoltConfig.call(
|
|
92
|
+
this,
|
|
93
|
+
`${relayURL}/discovery`,
|
|
94
|
+
);
|
|
95
|
+
if (this._voltConfig.relay.ca_pem) {
|
|
96
|
+
log(
|
|
97
|
+
"Auto-fetched Relay CA %s, remote address %s",
|
|
98
|
+
this._voltConfig.relay.ca_pem,
|
|
99
|
+
this._voltConfig.relay.address,
|
|
100
|
+
);
|
|
101
|
+
} else {
|
|
102
|
+
throw new Error("Unable to fetch Relay CA - cannot securely connect.");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!this._credential.cache.ca) {
|
|
107
|
+
this._credential.cache.ca = this._voltConfig.ca_pem;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!this._credential.cache.ca) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
"No certificate authority found in configuration for target Volt - check configuration",
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const keyPair = this._credential.getKey();
|
|
117
|
+
const publicKeyPem = pki.publicKeyToPem(keyPair.publicKey);
|
|
118
|
+
|
|
119
|
+
if (!this._credential.cache.bindIp) {
|
|
120
|
+
// Default the IP address if none is specified. This is used to set the SAN in the
|
|
121
|
+
// certificate issued by the Volt.
|
|
122
|
+
this._credential.cache.bindIp = ip.address();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this._credential.saveCache();
|
|
126
|
+
|
|
127
|
+
// Form the request message.
|
|
128
|
+
const bindRequest = {
|
|
129
|
+
binding_name: this._config.client_name,
|
|
130
|
+
public_key: publicKeyPem,
|
|
131
|
+
host: this._credential.cache.bindIp,
|
|
132
|
+
x509_credential: [],
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
if (this._credential.cache.cert) {
|
|
136
|
+
bindRequest.x509_credential = [
|
|
137
|
+
this._credential.cache.cert + this._credential.cache.ca,
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (this._voltConfig.challenge_code) {
|
|
142
|
+
bindRequest.challenge = voltUtils.signBase64(
|
|
143
|
+
keyPair.privateKey,
|
|
144
|
+
this._voltConfig.challenge_code,
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
log(
|
|
148
|
+
"****no Volt challenge code available**** => not sending challenge signature",
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// For remote connections, add our cloud-issued certificate as an additional credential.
|
|
153
|
+
if (this._config?.relay?.ca_pem && this._credential.cache.cloud_cert) {
|
|
154
|
+
bindRequest.x509_credential = bindRequest.x509_credential.concat(
|
|
155
|
+
this._credential.cache.cloud_cert + this._config.relay.ca_pem,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
log("sending %d x509 credentials", bindRequest.x509_credential.length);
|
|
160
|
+
log("bind TTL is ", this._config.bindRequestTTL);
|
|
161
|
+
|
|
162
|
+
// This will block while the request is pending.
|
|
163
|
+
const pollInterval = 10000;
|
|
164
|
+
let decision = "";
|
|
165
|
+
do {
|
|
166
|
+
if (decision) {
|
|
167
|
+
// Wait a while before retrying.
|
|
168
|
+
// eslint-disable-next-line no-await-in-loop
|
|
169
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
170
|
+
}
|
|
171
|
+
// eslint-disable-next-line no-await-in-loop
|
|
172
|
+
decision = await issueBind.call(
|
|
173
|
+
this,
|
|
174
|
+
bindRequest,
|
|
175
|
+
this._voltConfig.bindRequestTTL,
|
|
176
|
+
);
|
|
177
|
+
log("binding decision: %s", decision);
|
|
178
|
+
} while (
|
|
179
|
+
decision === "POLICY_DECISION_PROMPT" ||
|
|
180
|
+
decision === "POLICY_DECISION_PENDING"
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return decision === "POLICY_DECISION_PERMIT";
|
|
184
|
+
} catch (err) {
|
|
185
|
+
log("failure binding to Volt [%s]", err.message);
|
|
186
|
+
throw err;
|
|
187
|
+
}
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
export function connectInternal() {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
191
|
+
try {
|
|
192
|
+
if (this._voltConnection) {
|
|
193
|
+
log("connectInternal - Volt connection already exists");
|
|
194
|
+
return Promise.resolve();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const onDisconnected = () => {
|
|
198
|
+
this._connected = false;
|
|
199
|
+
this._session = null;
|
|
200
|
+
|
|
201
|
+
if (this._cachedClient) {
|
|
202
|
+
this._grpc.closeClient(this._cachedClient);
|
|
203
|
+
this._cachedClient = null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (this._cachedRemoteClient) {
|
|
207
|
+
this._grpc.closeClient(this._cachedRemoteClient);
|
|
208
|
+
this._cachedRemoteClient = null;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
this._voltConnection = new VoltConnection(this, this._grpc);
|
|
213
|
+
|
|
214
|
+
this._voltConnection.on("connected", (session) => {
|
|
215
|
+
this._session = session;
|
|
216
|
+
this._connected = !!session;
|
|
217
|
+
if (!this._connected) {
|
|
218
|
+
onDisconnected();
|
|
219
|
+
}
|
|
220
|
+
this.emit("connected", !!session);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
this._voltConnection.on("error", (err) => {
|
|
224
|
+
// Should auto-reconnect.
|
|
225
|
+
log("Volt connection error [%s]", err.message);
|
|
226
|
+
if (this._connected) {
|
|
227
|
+
onDisconnected();
|
|
228
|
+
this.emit("connected", false);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
this._voltConnection.on("evt", (evt) => {
|
|
233
|
+
this.emit("evt", evt);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
return this._voltConnection.connect();
|
|
237
|
+
} catch (err) {
|
|
238
|
+
log("connect - error [%s]", err.message);
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
export function getVoltAPIClientInternal() {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
244
|
+
if (!this._cachedClient) {
|
|
245
|
+
const serviceAddress = this.isRemote
|
|
246
|
+
? this._config.relay.address
|
|
247
|
+
: this._voltConfig.address;
|
|
248
|
+
log("creating cached VoltAPI service client on %s", serviceAddress);
|
|
249
|
+
let serviceDescriptors = {};
|
|
250
|
+
voltServices.forEach((pkg) => {
|
|
251
|
+
const packageDescriptors = getServiceDescriptors(this._grpc, pkg);
|
|
252
|
+
serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
this._cachedClient = createGrpcClient(
|
|
256
|
+
this._grpc,
|
|
257
|
+
serviceDescriptors,
|
|
258
|
+
serviceAddress,
|
|
259
|
+
this._credential,
|
|
260
|
+
this.isRemote && !this.isVoltRelay,
|
|
261
|
+
this._config?.relay?.cloud ? this._voltConfig.id : "",
|
|
262
|
+
this._config?.relay?.cloud ? this._voltConfig.id : "",
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return this._cachedClient;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
export function unaryCall(method, request) {
|
|
270
|
-
|
|
271
|
-
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
const grpcClient = this.getVoltAPIClient();
|
|
272
272
|
|
|
273
|
-
|
|
273
|
+
const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY");
|
|
274
274
|
|
|
275
|
-
|
|
275
|
+
let response = null;
|
|
276
276
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
call.on("data", (payload) => {
|
|
278
|
+
// Cache the response and wait for `end`.
|
|
279
|
+
response = payload;
|
|
280
|
+
});
|
|
281
281
|
|
|
282
|
-
|
|
282
|
+
call.on("error", reject);
|
|
283
283
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
284
|
+
call.on("end", () => {
|
|
285
|
+
if (!response) {
|
|
286
|
+
return reject(new Error("no response received"));
|
|
287
|
+
}
|
|
288
288
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
289
|
+
// Convert non-OK status to a JS error.
|
|
290
|
+
if (response.status?.message) {
|
|
291
|
+
reject(new Error(response.status.message));
|
|
292
|
+
} else {
|
|
293
|
+
resolve(response);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
296
|
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
call.start(grpcClient, request);
|
|
298
|
+
});
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
export async function fetchVoltConfig(discovery_url) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
302
|
+
try {
|
|
303
|
+
const getJSON = bent("json");
|
|
304
|
+
const fullVoltConfig = await getJSON(discovery_url);
|
|
305
|
+
|
|
306
|
+
if (!fullVoltConfig || typeof fullVoltConfig !== "object") {
|
|
307
|
+
throw new Error(`invalid config resolved from: ${discovery_url}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const voltConfig = pick(
|
|
311
|
+
fullVoltConfig,
|
|
312
|
+
"id",
|
|
313
|
+
"display_name",
|
|
314
|
+
"public_key",
|
|
315
|
+
"fingerprint",
|
|
316
|
+
"address",
|
|
317
|
+
"ca_pem",
|
|
318
|
+
"challenge_code",
|
|
319
|
+
"cloud",
|
|
320
|
+
"relay",
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
return voltConfig;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
throw new Error(
|
|
326
|
+
`Failure loading config from ${discovery_url}: ${err.message}`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
export async function fetchVoltConfigFromDID(volt_did) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
332
|
+
try {
|
|
333
|
+
const didResolution = voltUtils.getDIDResolutionURL(volt_did);
|
|
334
|
+
if (!didResolution) {
|
|
335
|
+
throw new Error(`Invalid Volt DID: ${volt_did}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const getJSON = bent("json");
|
|
339
|
+
const didDocument = await getJSON(didResolution);
|
|
340
|
+
const voltConfigServices = findDIDDocumentService(
|
|
341
|
+
didDocument,
|
|
342
|
+
constants.didServiceType.voltConfig,
|
|
343
|
+
);
|
|
344
|
+
if (voltConfigServices.length !== 1) {
|
|
345
|
+
log(
|
|
346
|
+
"Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
|
|
347
|
+
volt_did,
|
|
348
|
+
voltConfigServices.length,
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
|
|
353
|
+
return await fetchVoltConfig.call(this, serviceEndpoint);
|
|
354
|
+
} catch (err) {
|
|
355
|
+
throw new Error(
|
|
356
|
+
`Failure fetching Volt config from DID document ${volt_did}: ${err.message}`,
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
359
|
}
|