@tdxvolt/volt-client-grpc 0.1.2 → 0.11.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/lib/index.cjs +422 -356
- package/package.json +2 -2
- package/protobuf/tdx/volt_api/data/v1/sqlite.proto +13 -13
- package/protobuf/tdx/volt_api/volt/v1/volt.proto +3 -0
- package/protobuf/tdx/volt_api/volt/v1/wire_api.proto +1 -1
- package/src/grpc-call.js +269 -246
- package/src/volt-client-internal.js +93 -51
- package/src/volt-client.js +65 -64
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.11.0",
|
|
7
7
|
"description": "tdx Volt library for nodejs clients",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"exports": {
|
|
@@ -47,4 +47,4 @@
|
|
|
47
47
|
"node-forge": "^0.9.0",
|
|
48
48
|
"uuid": "^8.1.0"
|
|
49
49
|
}
|
|
50
|
-
}
|
|
50
|
+
}
|
|
@@ -3,19 +3,19 @@ syntax = "proto3";
|
|
|
3
3
|
package tdx.volt_api.data.v1;
|
|
4
4
|
|
|
5
5
|
// Just reflect SQLite types for now.
|
|
6
|
-
enum
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
enum DataType {
|
|
7
|
+
DATA_TYPE_UNKNOWN = 0;
|
|
8
|
+
DATA_TYPE_TEXT = 1;
|
|
9
|
+
DATA_TYPE_INTEGER = 2;
|
|
10
|
+
DATA_TYPE_REAL = 3;
|
|
11
|
+
DATA_TYPE_BLOB = 4;
|
|
12
|
+
DATA_TYPE_NULL = 5;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
message Column {
|
|
16
16
|
string name = 1;
|
|
17
17
|
string description = 2;
|
|
18
|
-
|
|
18
|
+
DataType type = 3;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
message Schema {
|
|
@@ -25,12 +25,12 @@ message Schema {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
message Variant {
|
|
28
|
-
ColumnType type = 1;
|
|
29
28
|
oneof data {
|
|
30
|
-
string text =
|
|
31
|
-
int64 integer =
|
|
32
|
-
double real =
|
|
33
|
-
bytes blob =
|
|
29
|
+
string text = 1;
|
|
30
|
+
int64 integer = 2;
|
|
31
|
+
double real = 3;
|
|
32
|
+
bytes blob = 4;
|
|
33
|
+
bool null = 5;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -82,6 +82,9 @@ message ProxyConnection {
|
|
|
82
82
|
|
|
83
83
|
// Set to indicate the Volt API itself is not automatically exposed to the connection.
|
|
84
84
|
bool disable_volt_api = 9;
|
|
85
|
+
|
|
86
|
+
// Optional challenge that can be presented in the bind phase when initialising the connection.
|
|
87
|
+
string challenge = 10;
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
enum SecureMode {
|
|
@@ -14,7 +14,7 @@ service WireAPI {
|
|
|
14
14
|
rpc SubscribeWire(stream SubscribeWireRequest) returns (stream SubscribeWireResponse);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
// Because the publish RPC is
|
|
17
|
+
// Because the publish RPC is streaming, the policy will not be checked until the first message arrives. This can mean that the `PublishWire` call appears to succeed but then fails after the first attempt to publish a message.
|
|
18
18
|
// To avoid this, clients can immediately send a message with the `wire_id` set and no `chunk` set. This will establish that the correct permissions are in place and fail fast if not.
|
|
19
19
|
message PublishWireRequest {
|
|
20
20
|
// Only necessary in the first payload.
|
package/src/grpc-call.js
CHANGED
|
@@ -1,262 +1,285 @@
|
|
|
1
1
|
/* eslint-disable no-underscore-dangle */
|
|
2
2
|
import debug from "debug";
|
|
3
3
|
import EventEmitter from "events";
|
|
4
|
-
import {VoltCredential} from "./volt-credential.js";
|
|
4
|
+
import { VoltCredential } from "./volt-credential.js";
|
|
5
5
|
|
|
6
6
|
const log = debug("volt-client-grpc:grpc-call");
|
|
7
7
|
|
|
8
|
-
function _prepareInvokeRequest(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
8
|
+
function _prepareInvokeRequest(
|
|
9
|
+
request,
|
|
10
|
+
method,
|
|
11
|
+
methodType = "METHOD_TYPE_UNARY",
|
|
12
|
+
) {
|
|
13
|
+
const meta = this._voltClient.credential.getIdentityMetadata(
|
|
14
|
+
this._voltClient.grpc,
|
|
15
|
+
this._voltClient.voltConfig.id,
|
|
16
|
+
this._voltClient.isRemote,
|
|
17
|
+
);
|
|
18
|
+
let invokeRequest;
|
|
19
|
+
let callMethod = method;
|
|
20
|
+
|
|
21
|
+
if (this._voltClient.isVoltRelay) {
|
|
22
|
+
let requestPayload;
|
|
23
|
+
if (request) {
|
|
24
|
+
requestPayload = this._grpcClient[method].requestSerialize(request);
|
|
25
|
+
} else if (request !== null) {
|
|
26
|
+
log("****************LOOKOUT****************** - empty request");
|
|
27
|
+
} else {
|
|
28
|
+
log("explicit empty request payload");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const tunnelMethod = this._grpcClient.Tunnel;
|
|
32
|
+
|
|
33
|
+
const tunnelResp = {
|
|
34
|
+
method_invoke: {
|
|
35
|
+
method_name: this._grpcClient[method].path,
|
|
36
|
+
method_type: methodType,
|
|
37
|
+
token: meta.token,
|
|
38
|
+
request: requestPayload,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const invokePayload = VoltCredential.aesEncrypt(
|
|
43
|
+
tunnelMethod.responseSerialize(tunnelResp),
|
|
44
|
+
meta.sharedKey.key,
|
|
45
|
+
meta.sharedKey.iv,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
log("target volt is %s", this._voltClient.credential.voltFingerprint);
|
|
49
|
+
|
|
50
|
+
invokeRequest = {
|
|
51
|
+
identity_fingerprint: this._voltClient.credential.voltFingerprint,
|
|
52
|
+
payload: invokePayload,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
callMethod = "Invoke";
|
|
56
|
+
} else if (this._voltClient.isRemote && meta.sharedKey.key) {
|
|
57
|
+
const requestPayload =
|
|
58
|
+
this._grpcClient[method].requestSerializeOriginal(request);
|
|
59
|
+
invokeRequest = VoltCredential.aesEncrypt(
|
|
60
|
+
requestPayload,
|
|
61
|
+
meta.sharedKey.key,
|
|
62
|
+
meta.sharedKey.iv,
|
|
63
|
+
);
|
|
64
|
+
} else {
|
|
65
|
+
invokeRequest = request;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
meta,
|
|
70
|
+
method: callMethod,
|
|
71
|
+
methodName: method,
|
|
72
|
+
request: invokeRequest,
|
|
73
|
+
};
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
function _preparePayloadRequest(request, invokeInfo) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
77
|
+
let payloadRequest;
|
|
78
|
+
|
|
79
|
+
if (this._voltClient.isVoltRelay) {
|
|
80
|
+
const requestPayload =
|
|
81
|
+
this._grpcClient[invokeInfo.methodName].requestSerialize(request);
|
|
82
|
+
const tunnelMethod = this._grpcClient.Tunnel;
|
|
83
|
+
|
|
84
|
+
const tunnelResp = {
|
|
85
|
+
method_payload: {
|
|
86
|
+
payload: requestPayload,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const invokePayload = VoltCredential.aesEncrypt(
|
|
91
|
+
tunnelMethod.responseSerialize(tunnelResp),
|
|
92
|
+
invokeInfo.meta.sharedKey.key,
|
|
93
|
+
invokeInfo.meta.sharedKey.iv,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
payloadRequest = {
|
|
97
|
+
payload: invokePayload,
|
|
98
|
+
};
|
|
99
|
+
} else if (this._voltClient.isRemote && invokeInfo.meta.sharedKey.key) {
|
|
100
|
+
const requestPayload =
|
|
101
|
+
this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(request);
|
|
102
|
+
payloadRequest = VoltCredential.aesEncrypt(
|
|
103
|
+
requestPayload,
|
|
104
|
+
invokeInfo.meta.sharedKey.key,
|
|
105
|
+
invokeInfo.meta.sharedKey.iv,
|
|
106
|
+
);
|
|
107
|
+
} else {
|
|
108
|
+
payloadRequest = request;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return payloadRequest;
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
function _parseResponse(method, meta, response) {
|
|
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
|
-
|
|
115
|
+
let invokeResponse = {};
|
|
116
|
+
|
|
117
|
+
if (this._voltClient.isVoltRelay) {
|
|
118
|
+
if (response.payload) {
|
|
119
|
+
const decryptedPayload = VoltCredential.aesDecrypt(
|
|
120
|
+
response.payload,
|
|
121
|
+
meta.sharedKey.key,
|
|
122
|
+
meta.sharedKey.iv,
|
|
123
|
+
);
|
|
124
|
+
const tunnelMethod = this._grpcClient.Tunnel;
|
|
125
|
+
const responsePayload = tunnelMethod.requestDeserialize(decryptedPayload);
|
|
126
|
+
if (responsePayload.payload === "method_payload") {
|
|
127
|
+
invokeResponse.payload = this._grpcClient[method].responseDeserialize(
|
|
128
|
+
responsePayload.method_payload.payload,
|
|
129
|
+
);
|
|
130
|
+
} else if (responsePayload.payload === "method_end") {
|
|
131
|
+
invokeResponse = responsePayload.method_end;
|
|
132
|
+
} else {
|
|
133
|
+
log("unexpected tunnel payload type: %s", responsePayload.payload);
|
|
134
|
+
}
|
|
135
|
+
} else if (response.status?.message) {
|
|
136
|
+
// There's been an error in the tunnel.
|
|
137
|
+
log(
|
|
138
|
+
"Tunnel status received: %s, code %d, description: %s",
|
|
139
|
+
response.status.message,
|
|
140
|
+
response.status.code,
|
|
141
|
+
response.status.description || "n/a",
|
|
142
|
+
);
|
|
143
|
+
invokeResponse = {
|
|
144
|
+
ended: true,
|
|
145
|
+
error: `Error in tunnel: ${response.status.message}`,
|
|
146
|
+
};
|
|
147
|
+
} else {
|
|
148
|
+
invokeResponse.methodId = response.invoke_id;
|
|
149
|
+
log("started method %d for %s", invokeResponse.methodId, method);
|
|
150
|
+
}
|
|
151
|
+
} else if (this._voltClient.isRemote && meta.sharedKey.key) {
|
|
152
|
+
const decryptedPayload = VoltCredential.aesDecrypt(
|
|
153
|
+
response,
|
|
154
|
+
meta.sharedKey.key,
|
|
155
|
+
meta.sharedKey.iv,
|
|
156
|
+
);
|
|
157
|
+
invokeResponse = {
|
|
158
|
+
payload:
|
|
159
|
+
this._grpcClient[method].responseDeserializeOriginal(decryptedPayload),
|
|
160
|
+
};
|
|
161
|
+
} else {
|
|
162
|
+
invokeResponse = { payload: response };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return invokeResponse;
|
|
139
166
|
}
|
|
140
167
|
|
|
141
168
|
export default class GRPCCall extends EventEmitter {
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return this._call.write(this._initialRequest.request);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
169
|
+
constructor(voltClient, methodName, methodType) {
|
|
170
|
+
super();
|
|
171
|
+
this._methodName = methodName;
|
|
172
|
+
this._methodType = methodType;
|
|
173
|
+
this._call = null;
|
|
174
|
+
this._voltClient = voltClient;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
start(grpcClient, request) {
|
|
178
|
+
this._grpcClient = grpcClient;
|
|
179
|
+
|
|
180
|
+
this._initialRequest = _prepareInvokeRequest.call(
|
|
181
|
+
this,
|
|
182
|
+
request,
|
|
183
|
+
this._methodName,
|
|
184
|
+
this._methodType,
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
if (!this._grpcClient[this._initialRequest.method]) {
|
|
188
|
+
return reject(
|
|
189
|
+
new Error(`method not found: '${this._initialRequest.method}'`),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this._call = this._grpcClient[this._initialRequest.method](
|
|
194
|
+
this._initialRequest.meta.metadata,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
this._call.on("data", (streamResponse) => {
|
|
198
|
+
try {
|
|
199
|
+
const parsedResponse = _parseResponse.call(
|
|
200
|
+
this,
|
|
201
|
+
this._methodName,
|
|
202
|
+
this._initialRequest.meta,
|
|
203
|
+
streamResponse,
|
|
204
|
+
);
|
|
205
|
+
if (parsedResponse.payload) {
|
|
206
|
+
this.emit("data", parsedResponse.payload);
|
|
207
|
+
} else if (parsedResponse.ended) {
|
|
208
|
+
if (parsedResponse.error) {
|
|
209
|
+
this.emit("error", new Error(parsedResponse.error));
|
|
210
|
+
} else {
|
|
211
|
+
// Not sure this is necessary - we should receive "end" from grpc soon...
|
|
212
|
+
this.emit("end");
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
log("method id is %s", parsedResponse.methodId);
|
|
216
|
+
}
|
|
217
|
+
} catch (err) {
|
|
218
|
+
log("failure processing connect response: %s", err.message);
|
|
219
|
+
this.emit("error", err);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
this._call.on("error", (err) => {
|
|
224
|
+
log("ERROR - intercepted stream error %s", err.message);
|
|
225
|
+
this.emit("error", err);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
this._call.on("finish", () => {
|
|
229
|
+
// The read side has ended (i.e. we called end()).
|
|
230
|
+
log("finished call %s", this._methodName);
|
|
231
|
+
this.emit("finish");
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
this._call.on("end", () => {
|
|
235
|
+
// The remote peer ended the stream.
|
|
236
|
+
log(
|
|
237
|
+
"ended call %s, method id: %s",
|
|
238
|
+
this._methodName,
|
|
239
|
+
this._initialRequest.methodId || "n/a - not tunnelling",
|
|
240
|
+
);
|
|
241
|
+
this.emit("end");
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (this._initialRequest.request) {
|
|
245
|
+
this._call.write(this._initialRequest.request);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
end() {
|
|
252
|
+
if (this._call) {
|
|
253
|
+
return this._call.end();
|
|
254
|
+
} else {
|
|
255
|
+
throw new Error("call not initialised");
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
get writable() {
|
|
260
|
+
return this._call?.writable;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
write(request) {
|
|
264
|
+
if (!this._call) {
|
|
265
|
+
log("ERROR - call not initialised");
|
|
266
|
+
throw new Error("call not initialised");
|
|
267
|
+
} else if (this._initialRequest) {
|
|
268
|
+
const payloadRequest = _preparePayloadRequest.call(
|
|
269
|
+
this,
|
|
270
|
+
request,
|
|
271
|
+
this._initialRequest,
|
|
272
|
+
);
|
|
273
|
+
return this._call.write(payloadRequest);
|
|
274
|
+
} else {
|
|
275
|
+
this._initialRequest = _prepareInvokeRequest.call(
|
|
276
|
+
this,
|
|
277
|
+
request,
|
|
278
|
+
this._methodName,
|
|
279
|
+
this._methodType,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
return this._call.write(this._initialRequest.request);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
262
285
|
}
|