@onekeyfe/hd-transport 1.1.34-alpha.2 → 1.1.34-alpha.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/__tests__/build-receive.test.js +6 -8
- package/__tests__/decode-features.test.js +3 -2
- package/__tests__/messages.test.js +131 -0
- package/__tests__/protocol-v2-link-manager.test.js +351 -0
- package/__tests__/protocol-v2-usb-transport-base.test.js +296 -0
- package/__tests__/protocol-v2.test.js +899 -0
- package/dist/constants.d.ts +16 -5
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +1398 -45
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1102 -85
- package/dist/protocols/index.d.ts +50 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/v1/decode.d.ts +11 -0
- package/dist/protocols/v1/decode.d.ts.map +1 -0
- package/dist/protocols/v1/encode.d.ts +11 -0
- package/dist/protocols/v1/encode.d.ts.map +1 -0
- package/dist/protocols/v1/index.d.ts +5 -0
- package/dist/protocols/v1/index.d.ts.map +1 -0
- package/dist/protocols/v1/packets.d.ts +7 -0
- package/dist/protocols/v1/packets.d.ts.map +1 -0
- package/dist/{serialization → protocols/v1}/receive.d.ts +1 -1
- package/dist/protocols/v1/receive.d.ts.map +1 -0
- package/dist/protocols/v2/constants.d.ts +8 -0
- package/dist/protocols/v2/constants.d.ts.map +1 -0
- package/dist/protocols/v2/crc8.d.ts +3 -0
- package/dist/protocols/v2/crc8.d.ts.map +1 -0
- package/dist/protocols/v2/decode.d.ts +8 -0
- package/dist/protocols/v2/decode.d.ts.map +1 -0
- package/dist/protocols/v2/encode.d.ts +4 -0
- package/dist/protocols/v2/encode.d.ts.map +1 -0
- package/dist/protocols/v2/frame-assembler.d.ts +12 -0
- package/dist/protocols/v2/frame-assembler.d.ts.map +1 -0
- package/dist/protocols/v2/index.d.ts +6 -0
- package/dist/protocols/v2/index.d.ts.map +1 -0
- package/dist/protocols/v2/link-manager.d.ts +36 -0
- package/dist/protocols/v2/link-manager.d.ts.map +1 -0
- package/dist/protocols/v2/sequence-cursor.d.ts +5 -0
- package/dist/protocols/v2/sequence-cursor.d.ts.map +1 -0
- package/dist/protocols/v2/session.d.ts +63 -0
- package/dist/protocols/v2/session.d.ts.map +1 -0
- package/dist/protocols/v2/usb-transport-base.d.ts +31 -0
- package/dist/protocols/v2/usb-transport-base.d.ts.map +1 -0
- package/dist/serialization/index.d.ts +6 -3
- package/dist/serialization/index.d.ts.map +1 -1
- package/dist/serialization/protobuf/decode.d.ts.map +1 -1
- package/dist/serialization/protobuf/messages.d.ts +1 -1
- package/dist/serialization/protobuf/messages.d.ts.map +1 -1
- package/dist/types/messages.d.ts +707 -12
- package/dist/types/messages.d.ts.map +1 -1
- package/dist/types/transport.d.ts +19 -5
- package/dist/types/transport.d.ts.map +1 -1
- package/dist/utils/logBlockCommand.d.ts.map +1 -1
- package/messages-protocol-v2.json +13544 -0
- package/package.json +3 -3
- package/scripts/protobuf-patches/TxInputType.js +1 -0
- package/scripts/protobuf-patches/index.js +2 -0
- package/scripts/protobuf-types.js +233 -18
- package/src/constants.ts +48 -6
- package/src/index.ts +47 -11
- package/src/protocols/index.ts +124 -0
- package/src/{serialization/protocol → protocols/v1}/decode.ts +4 -4
- package/src/{serialization/protocol → protocols/v1}/encode.ts +18 -13
- package/src/protocols/v1/index.ts +4 -0
- package/src/protocols/v1/packets.ts +53 -0
- package/src/{serialization → protocols/v1}/receive.ts +5 -5
- package/src/protocols/v2/constants.ts +7 -0
- package/src/protocols/v2/crc8.ts +34 -0
- package/src/protocols/v2/decode.ts +89 -0
- package/src/protocols/v2/encode.ts +90 -0
- package/src/protocols/v2/frame-assembler.ts +98 -0
- package/src/protocols/v2/index.ts +5 -0
- package/src/protocols/v2/link-manager.ts +162 -0
- package/src/protocols/v2/sequence-cursor.ts +10 -0
- package/src/protocols/v2/session.ts +339 -0
- package/src/protocols/v2/usb-transport-base.ts +194 -0
- package/src/serialization/index.ts +6 -5
- package/src/serialization/protobuf/decode.ts +7 -0
- package/src/serialization/protobuf/messages.ts +14 -5
- package/src/types/messages.ts +903 -13
- package/src/types/transport.ts +29 -5
- package/src/utils/logBlockCommand.ts +9 -1
- package/dist/serialization/protocol/decode.d.ts +0 -11
- package/dist/serialization/protocol/decode.d.ts.map +0 -1
- package/dist/serialization/protocol/encode.d.ts +0 -11
- package/dist/serialization/protocol/encode.d.ts.map +0 -1
- package/dist/serialization/protocol/index.d.ts +0 -3
- package/dist/serialization/protocol/index.d.ts.map +0 -1
- package/dist/serialization/receive.d.ts.map +0 -1
- package/dist/serialization/send.d.ts +0 -7
- package/dist/serialization/send.d.ts.map +0 -1
- package/src/serialization/protocol/index.ts +0 -2
- package/src/serialization/send.ts +0 -58
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport",
|
|
3
|
-
"version": "1.1.34-alpha.
|
|
3
|
+
"version": "1.1.34-alpha.3",
|
|
4
4
|
"description": "Transport layer abstractions and utilities for OneKey hardware SDK.",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"test": "jest",
|
|
17
17
|
"lint": "eslint .",
|
|
18
18
|
"lint:fix": "eslint . --fix",
|
|
19
|
-
"update
|
|
19
|
+
"update-protobuf": "./scripts/protobuf-build.sh"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/bytebuffer": "^5.0.42",
|
|
@@ -28,5 +28,5 @@
|
|
|
28
28
|
"long": "^4.0.0",
|
|
29
29
|
"protobufjs": "^6.11.2"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "3688bc8495eeb2ffd5616270d13fa29e8967623a"
|
|
32
32
|
}
|
|
@@ -29,6 +29,7 @@ type CommonTxInputType = {|
|
|
|
29
29
|
witness?: string, // used by EXTERNAL, depending on script_pubkey
|
|
30
30
|
ownership_proof?: string, // used by EXTERNAL, depending on script_pubkey
|
|
31
31
|
commitment_data?: string, // used by EXTERNAL, depending on ownership_proof
|
|
32
|
+
coinjoin_flags?: number, // Protocol V2 only (CoinJoin)
|
|
32
33
|
|};
|
|
33
34
|
|
|
34
35
|
export type TxInputType =
|
|
@@ -116,6 +116,7 @@ const TYPE_PATCH = {
|
|
|
116
116
|
'Features.onekey_se03_state': 'string | null',
|
|
117
117
|
'Features.onekey_se04_state': 'string | null',
|
|
118
118
|
'HDNodePathType.node': 'HDNodeType | string',
|
|
119
|
+
'FilesystemFile.data': 'Buffer | ArrayBuffer | Uint8Array | string',
|
|
119
120
|
'FirmwareUpload.payload': 'Buffer | ArrayBuffer',
|
|
120
121
|
'EthereumGetAddress.encoded_network': 'ArrayBuffer',
|
|
121
122
|
'EthereumDefinitions.encoded_network': 'ArrayBuffer',
|
|
@@ -207,6 +208,7 @@ const TYPE_PATCH = {
|
|
|
207
208
|
'TonSignMessage.fwd_fee': UINT_TYPE,
|
|
208
209
|
'TonSignMessage.expire_at': UINT_TYPE,
|
|
209
210
|
'TonSignMessage.ext_ton_amount': UINT_TYPE,
|
|
211
|
+
'TonSignData.timestamp': UINT_TYPE,
|
|
210
212
|
'TonSignProof.expire_at': UINT_TYPE,
|
|
211
213
|
'TonSignData.timestamp': UINT_TYPE,
|
|
212
214
|
};
|
|
@@ -6,9 +6,33 @@
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
|
|
9
|
-
const json = require('../messages.json');
|
|
10
9
|
const { RULE_PATCH, TYPE_PATCH, DEFINITION_PATCH, SKIP, UINT_TYPE } = require('./protobuf-patches');
|
|
11
10
|
|
|
11
|
+
const readJson = filePath => JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
12
|
+
|
|
13
|
+
const localMessagesJsonPath = path.join(__dirname, '../messages.json');
|
|
14
|
+
const coreMessagesJsonPath = path.join(__dirname, '../../core/src/data/messages/messages.json');
|
|
15
|
+
const json = readJson(
|
|
16
|
+
fs.existsSync(localMessagesJsonPath) ? localMessagesJsonPath : coreMessagesJsonPath
|
|
17
|
+
);
|
|
18
|
+
const optionalJsonFiles = ['../messages-protocol-v2.json'];
|
|
19
|
+
const OPTIONAL_DUPLICATE_TYPE_ALIASES = {
|
|
20
|
+
DeviceInfo: 'ProtocolV2DeviceInfo',
|
|
21
|
+
};
|
|
22
|
+
const OPTIONAL_NESTED_TYPE_ALIASES = [
|
|
23
|
+
{ parent: 'Failure', name: 'FailureType', alias: 'ProtocolV2FailureType' },
|
|
24
|
+
{ parent: 'Features', name: 'Capability', alias: 'ProtocolV2Capability' },
|
|
25
|
+
];
|
|
26
|
+
const messageTypeAliases = {};
|
|
27
|
+
const skipMessageTypeKeys = new Set(Object.values(OPTIONAL_DUPLICATE_TYPE_ALIASES));
|
|
28
|
+
|
|
29
|
+
// V1/V2 duplicate-type merge bookkeeping.
|
|
30
|
+
// When the same top-level type exists in both schemas with a different shape we emit
|
|
31
|
+
// the union of both sides instead of silently keeping the V1 definition only.
|
|
32
|
+
const mergedRuleOverrides = {}; // `${Type}.${field}` -> 'optional'
|
|
33
|
+
const mergedTypeOverrides = {}; // `${Type}.${field}` -> 'v1JsType | v2JsType'
|
|
34
|
+
const duplicateTypeMergeReport = []; // { name, diffs: string[] }
|
|
35
|
+
|
|
12
36
|
const args = process.argv.slice(2);
|
|
13
37
|
|
|
14
38
|
const isTypescript = args.includes('typescript');
|
|
@@ -26,6 +50,8 @@ const FIELD_TYPES = {
|
|
|
26
50
|
|
|
27
51
|
const types = []; // { type: 'enum | message', name: string, value: string[], exact?: boolean };
|
|
28
52
|
|
|
53
|
+
const hasParsedType = name => types.some(t => t && t.name === name);
|
|
54
|
+
|
|
29
55
|
// enums used as keys (string), used as values (number) by default
|
|
30
56
|
const ENUM_KEYS = [
|
|
31
57
|
'InputScriptType',
|
|
@@ -40,6 +66,7 @@ const ENUM_KEYS = [
|
|
|
40
66
|
'ButtonRequestType',
|
|
41
67
|
'PinMatrixRequestType',
|
|
42
68
|
'WordRequestType',
|
|
69
|
+
'ProtocolV2Capability',
|
|
43
70
|
];
|
|
44
71
|
|
|
45
72
|
const parseEnumTypescript = (itemName, item) => {
|
|
@@ -114,8 +141,9 @@ const useDefinition = def => {
|
|
|
114
141
|
return clean.replace(/\/\/ @typescript-variant(.*)/, '').replace(/\/\/ @flowtype-variant:/, '');
|
|
115
142
|
};
|
|
116
143
|
|
|
117
|
-
const parseMessage = (messageName, message, depth = 0) => {
|
|
144
|
+
const parseMessage = (messageName, message, depth = 0, skipExisting = false) => {
|
|
118
145
|
if (messageName === 'google') return;
|
|
146
|
+
if (!depth && skipExisting && hasParsedType(messageName)) return;
|
|
119
147
|
const value = [];
|
|
120
148
|
// add comment line
|
|
121
149
|
if (!depth) value.push(`// ${messageName}`);
|
|
@@ -123,7 +151,9 @@ const parseMessage = (messageName, message, depth = 0) => {
|
|
|
123
151
|
|
|
124
152
|
// declare nested values
|
|
125
153
|
if (message.nested) {
|
|
126
|
-
Object.keys(message.nested).map(item =>
|
|
154
|
+
Object.keys(message.nested).map(item =>
|
|
155
|
+
parseMessage(item, message.nested[item], depth + 1, skipExisting)
|
|
156
|
+
);
|
|
127
157
|
}
|
|
128
158
|
|
|
129
159
|
if (message.values) {
|
|
@@ -145,11 +175,15 @@ const parseMessage = (messageName, message, depth = 0) => {
|
|
|
145
175
|
Object.keys(message.fields).forEach(fieldName => {
|
|
146
176
|
const field = message.fields[fieldName];
|
|
147
177
|
const fieldKey = `${messageName}.${fieldName}`;
|
|
148
|
-
// find patch for "rule"
|
|
149
|
-
const fieldRule = RULE_PATCH[fieldKey] || field.rule;
|
|
178
|
+
// find patch for "rule" (hand-written patches win over V1/V2 merge overrides)
|
|
179
|
+
const fieldRule = RULE_PATCH[fieldKey] || mergedRuleOverrides[fieldKey] || field.rule;
|
|
150
180
|
const rule = fieldRule === 'required' || fieldRule === 'repeated' ? ': ' : '?: ';
|
|
151
|
-
// find patch for "type"
|
|
152
|
-
let type =
|
|
181
|
+
// find patch for "type" (hand-written patches win over V1/V2 merge overrides)
|
|
182
|
+
let type =
|
|
183
|
+
TYPE_PATCH[fieldKey] ||
|
|
184
|
+
mergedTypeOverrides[fieldKey] ||
|
|
185
|
+
FIELD_TYPES[field.type] ||
|
|
186
|
+
field.type;
|
|
153
187
|
// automatically convert all amount and fee fields to UINT_TYPE
|
|
154
188
|
if (['amount', 'fee'].includes(fieldName)) {
|
|
155
189
|
type = UINT_TYPE;
|
|
@@ -176,8 +210,163 @@ const parseMessage = (messageName, message, depth = 0) => {
|
|
|
176
210
|
});
|
|
177
211
|
};
|
|
178
212
|
|
|
213
|
+
const jsFieldType = protoType => FIELD_TYPES[protoType] || protoType;
|
|
214
|
+
|
|
215
|
+
// Merge a duplicate Protocol V2 definition into its V1 counterpart (in-memory only,
|
|
216
|
+
// messages.json / messages-protocol-v2.json on disk stay untouched).
|
|
217
|
+
// Strategy:
|
|
218
|
+
// - messages: emit the union of both sides' fields; a field present on one side only
|
|
219
|
+
// becomes optional
|
|
220
|
+
// - field type conflict: emit a union type and warn
|
|
221
|
+
// - field rule conflict (required/optional/repeated): keep the V1 rule and warn
|
|
222
|
+
// - enums: emit the union of members; member value conflicts keep the V1 value and warn
|
|
223
|
+
// - nested types are merged recursively
|
|
224
|
+
const mergeDuplicateDefinition = (typeName, base, extra, diffs) => {
|
|
225
|
+
if (base.values || extra.values) {
|
|
226
|
+
if (!base.values || !extra.values) {
|
|
227
|
+
diffs.push('! kind conflict (enum vs message), keeping V1 definition');
|
|
228
|
+
console.warn(
|
|
229
|
+
`[protobuf-types] duplicate type "${typeName}" is an enum on one side and a message on the other, keeping V1`
|
|
230
|
+
);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
Object.entries(extra.values).forEach(([memberName, memberValue]) => {
|
|
234
|
+
if (!(memberName in base.values)) {
|
|
235
|
+
base.values[memberName] = memberValue;
|
|
236
|
+
diffs.push(`+ enum member ${memberName} = ${memberValue} (V2 only)`);
|
|
237
|
+
} else if (base.values[memberName] !== memberValue) {
|
|
238
|
+
diffs.push(
|
|
239
|
+
`! enum member ${memberName} value conflict: V1=${base.values[memberName]} V2=${memberValue}, keeping V1`
|
|
240
|
+
);
|
|
241
|
+
console.warn(
|
|
242
|
+
`[protobuf-types] enum "${typeName}.${memberName}" value differs between V1 (${base.values[memberName]}) and V2 (${memberValue}), keeping V1`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (extra.nested) {
|
|
250
|
+
base.nested = base.nested || {};
|
|
251
|
+
Object.keys(extra.nested).forEach(nestedName => {
|
|
252
|
+
if (base.nested[nestedName]) {
|
|
253
|
+
mergeDuplicateDefinition(
|
|
254
|
+
nestedName,
|
|
255
|
+
base.nested[nestedName],
|
|
256
|
+
extra.nested[nestedName],
|
|
257
|
+
diffs
|
|
258
|
+
);
|
|
259
|
+
} else {
|
|
260
|
+
base.nested[nestedName] = extra.nested[nestedName];
|
|
261
|
+
diffs.push(`+ nested type ${nestedName} (V2 only)`);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const extraFields = extra.fields || {};
|
|
267
|
+
if (!Object.keys(extraFields).length && !base.fields) return;
|
|
268
|
+
base.fields = base.fields || {};
|
|
269
|
+
const baseFields = base.fields;
|
|
270
|
+
|
|
271
|
+
Object.entries(extraFields).forEach(([fieldName, field]) => {
|
|
272
|
+
const fieldKey = `${typeName}.${fieldName}`;
|
|
273
|
+
if (!(fieldName in baseFields)) {
|
|
274
|
+
baseFields[fieldName] = field;
|
|
275
|
+
// field exists in V2 only -> always optional in the merged interface
|
|
276
|
+
if (field.rule) mergedRuleOverrides[fieldKey] = 'optional';
|
|
277
|
+
diffs.push(
|
|
278
|
+
`+ field ${fieldName}?: ${jsFieldType(field.type)}${
|
|
279
|
+
field.rule === 'repeated' ? '[]' : ''
|
|
280
|
+
} (V2 only, marked optional)`
|
|
281
|
+
);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const baseField = baseFields[fieldName];
|
|
285
|
+
if (baseField.type !== field.type) {
|
|
286
|
+
const baseJsType = jsFieldType(baseField.type);
|
|
287
|
+
const extraJsType = jsFieldType(field.type);
|
|
288
|
+
if (baseJsType !== extraJsType) {
|
|
289
|
+
mergedTypeOverrides[fieldKey] = `${baseJsType} | ${extraJsType}`;
|
|
290
|
+
diffs.push(
|
|
291
|
+
`! field ${fieldName} type conflict: V1=${baseField.type} V2=${field.type}, emitting union ${baseJsType} | ${extraJsType}`
|
|
292
|
+
);
|
|
293
|
+
console.warn(
|
|
294
|
+
`[protobuf-types] field "${fieldKey}" type differs between V1 (${baseField.type}) and V2 (${field.type}), emitting union type`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if ((baseField.rule || 'optional') !== (field.rule || 'optional')) {
|
|
299
|
+
diffs.push(
|
|
300
|
+
`! field ${fieldName} rule conflict: V1=${baseField.rule || 'optional'} V2=${
|
|
301
|
+
field.rule || 'optional'
|
|
302
|
+
}, keeping V1`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
Object.entries(baseFields).forEach(([fieldName, field]) => {
|
|
308
|
+
if (fieldName in extraFields) return;
|
|
309
|
+
const fieldKey = `${typeName}.${fieldName}`;
|
|
310
|
+
// field exists in V1 only -> always optional in the merged interface
|
|
311
|
+
if (field.rule && !mergedRuleOverrides[fieldKey]) {
|
|
312
|
+
mergedRuleOverrides[fieldKey] = 'optional';
|
|
313
|
+
diffs.push(`~ field ${fieldName} (V1 only): ${field.rule} -> optional`);
|
|
314
|
+
} else {
|
|
315
|
+
diffs.push(`~ field ${fieldName} (V1 only, already optional)`);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// Pass 1 (before parsing): merge duplicate V2 definitions into the V1 schema so the
|
|
321
|
+
// generated interfaces contain the union of both protocols' fields. Previously the V2
|
|
322
|
+
// side of a duplicate type was silently dropped, hiding V2-only fields/enum members.
|
|
323
|
+
const optionalJsons = [];
|
|
324
|
+
optionalJsonFiles.forEach(jsonFile => {
|
|
325
|
+
const jsonPath = path.join(__dirname, jsonFile);
|
|
326
|
+
if (!fs.existsSync(jsonPath)) return;
|
|
327
|
+
const optionalJson = readJson(jsonPath);
|
|
328
|
+
OPTIONAL_NESTED_TYPE_ALIASES.forEach(({ parent, name, alias }) => {
|
|
329
|
+
const definition = optionalJson.nested[parent]?.nested?.[name];
|
|
330
|
+
if (definition) optionalJson.nested[alias] = definition;
|
|
331
|
+
});
|
|
332
|
+
optionalJsons.push(optionalJson);
|
|
333
|
+
Object.keys(optionalJson.nested).forEach(e => {
|
|
334
|
+
if (!json.nested[e]) return; // V2-only type, parsed after the V1 pass below
|
|
335
|
+
if (OPTIONAL_DUPLICATE_TYPE_ALIASES[e]) return; // kept as a separate aliased type
|
|
336
|
+
if (SKIP.includes(e)) return; // custom/hand-written definitions (MessageType, TxInput, ...)
|
|
337
|
+
const diffs = [];
|
|
338
|
+
mergeDuplicateDefinition(e, json.nested[e], optionalJson.nested[e], diffs);
|
|
339
|
+
if (diffs.length) duplicateTypeMergeReport.push({ name: e, diffs });
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Explicit drift report: every merged duplicate type and its V1/V2 differences.
|
|
344
|
+
if (duplicateTypeMergeReport.length) {
|
|
345
|
+
console.log(
|
|
346
|
+
`[protobuf-types] ${duplicateTypeMergeReport.length} duplicate V1/V2 type(s) differ and were merged (field union):`
|
|
347
|
+
);
|
|
348
|
+
duplicateTypeMergeReport.forEach(({ name, diffs }) => {
|
|
349
|
+
console.log(` - ${name}`);
|
|
350
|
+
diffs.forEach(diff => console.log(` ${diff}`));
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
179
354
|
// top level messages and nested messages
|
|
180
|
-
Object.keys(json.nested).
|
|
355
|
+
Object.keys(json.nested).forEach(e => parseMessage(e, json.nested[e]));
|
|
356
|
+
|
|
357
|
+
// Pass 2: aliased duplicates (e.g. DeviceInfo -> ProtocolV2DeviceInfo) and V2-only types
|
|
358
|
+
optionalJsons.forEach(optionalJson => {
|
|
359
|
+
Object.keys(optionalJson.nested).forEach(e => {
|
|
360
|
+
const alias = hasParsedType(e) ? OPTIONAL_DUPLICATE_TYPE_ALIASES[e] : undefined;
|
|
361
|
+
if (alias) {
|
|
362
|
+
parseMessage(alias, optionalJson.nested[e]);
|
|
363
|
+
messageTypeAliases[e] = [e, alias];
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (json.nested[e]) return; // duplicate type, already merged into the V1 definition
|
|
367
|
+
parseMessage(e, optionalJson.nested[e], 0, true);
|
|
368
|
+
});
|
|
369
|
+
});
|
|
181
370
|
|
|
182
371
|
// types needs reordering (used before defined)
|
|
183
372
|
const ORDER = {
|
|
@@ -226,10 +415,14 @@ if (!isTypescript) {
|
|
|
226
415
|
types
|
|
227
416
|
.flatMap(t => (t && t.type === 'message' ? [t] : []))
|
|
228
417
|
.forEach(t => {
|
|
418
|
+
if (skipMessageTypeKeys.has(t.name)) return;
|
|
419
|
+
const messageTypeValue = messageTypeAliases[t.name]
|
|
420
|
+
? messageTypeAliases[t.name].join(' | ')
|
|
421
|
+
: t.name;
|
|
229
422
|
if (t.exact) {
|
|
230
|
-
lines.push(` ${t.name}: $Exact<${
|
|
423
|
+
lines.push(` ${t.name}: $Exact<${messageTypeValue}>;`);
|
|
231
424
|
} else {
|
|
232
|
-
lines.push(` ${t.name}: ${
|
|
425
|
+
lines.push(` ${t.name}: ${messageTypeValue};`);
|
|
233
426
|
}
|
|
234
427
|
// lines.push(' ' + t.name + ': $Exact<' + t.name + '>;');
|
|
235
428
|
});
|
|
@@ -244,11 +437,18 @@ export type MessageResponse<T: MessageKey> = {
|
|
|
244
437
|
message: $ElementType<MessageType, T>;
|
|
245
438
|
};
|
|
246
439
|
|
|
247
|
-
export type TypedCall =
|
|
440
|
+
export type TypedCall = {
|
|
441
|
+
<T: MessageKey, R: $ReadOnlyArray<MessageKey>>(
|
|
442
|
+
type: T,
|
|
443
|
+
resType: R,
|
|
444
|
+
message?: $ElementType<MessageType, T>
|
|
445
|
+
): Promise<MessageResponse<$ElementType<R, number>>>;
|
|
446
|
+
<T: MessageKey, R: MessageKey>(
|
|
248
447
|
type: T,
|
|
249
448
|
resType: R,
|
|
250
449
|
message?: $ElementType<MessageType, T>
|
|
251
|
-
)
|
|
450
|
+
): Promise<MessageResponse<R>>;
|
|
451
|
+
};
|
|
252
452
|
`);
|
|
253
453
|
} else {
|
|
254
454
|
lines.push('// custom connect definitions');
|
|
@@ -256,7 +456,11 @@ export type TypedCall = <T: MessageKey, R: MessageKey>(
|
|
|
256
456
|
types
|
|
257
457
|
.flatMap(t => (t && t.type === 'message' ? [t] : []))
|
|
258
458
|
.forEach(t => {
|
|
259
|
-
|
|
459
|
+
if (skipMessageTypeKeys.has(t.name)) return;
|
|
460
|
+
const messageTypeValue = messageTypeAliases[t.name]
|
|
461
|
+
? messageTypeAliases[t.name].join(' | ')
|
|
462
|
+
: t.name;
|
|
463
|
+
lines.push(` ${t.name}: ${messageTypeValue};`);
|
|
260
464
|
});
|
|
261
465
|
lines.push('};');
|
|
262
466
|
|
|
@@ -269,11 +473,22 @@ export type MessageResponse<T extends MessageKey> = {
|
|
|
269
473
|
message: MessageType[T];
|
|
270
474
|
};
|
|
271
475
|
|
|
272
|
-
export type
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
476
|
+
export type MessageResponseMap = {
|
|
477
|
+
[K in MessageKey]: MessageResponse<K>;
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
export type TypedCall = {
|
|
481
|
+
<T extends MessageKey, R extends readonly MessageKey[]>(
|
|
482
|
+
type: T,
|
|
483
|
+
resType: R,
|
|
484
|
+
message?: MessageType[T],
|
|
485
|
+
): Promise<MessageResponseMap[R[number]]>;
|
|
486
|
+
<T extends MessageKey, R extends MessageKey>(
|
|
487
|
+
type: T,
|
|
488
|
+
resType: R,
|
|
489
|
+
message?: MessageType[T],
|
|
490
|
+
): Promise<MessageResponse<R>>;
|
|
491
|
+
};
|
|
277
492
|
`);
|
|
278
493
|
}
|
|
279
494
|
|
package/src/constants.ts
CHANGED
|
@@ -1,8 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export const
|
|
1
|
+
// ---- Protocol V1 (Pro1 / Touch / Mini / Classic) ----
|
|
2
|
+
|
|
3
|
+
/** Protocol V1 USB report marker, ASCII `?`. */
|
|
4
|
+
export const PROTOCOL_V1_REPORT_ID = 0x3f;
|
|
5
|
+
|
|
6
|
+
/** Protocol V1 envelope marker, ASCII `#`. */
|
|
7
|
+
export const PROTOCOL_V1_HEADER_BYTE = 0x23;
|
|
8
|
+
|
|
9
|
+
/** Protocol V1 payload bytes per chunk after the report marker. */
|
|
10
|
+
export const PROTOCOL_V1_CHUNK_PAYLOAD_SIZE = 63;
|
|
11
|
+
|
|
12
|
+
/** Protocol V1 USB packet length: report marker plus chunk payload. */
|
|
13
|
+
export const PROTOCOL_V1_USB_PACKET_SIZE = PROTOCOL_V1_CHUNK_PAYLOAD_SIZE + 1;
|
|
14
|
+
|
|
15
|
+
/** Protocol V1 message metadata: two-byte type plus four-byte payload length. */
|
|
16
|
+
export const PROTOCOL_V1_MESSAGE_HEADER_SIZE = 2 + 4;
|
|
17
|
+
|
|
18
|
+
/** Protocol V1 envelope metadata: `##`, message type, and payload length. */
|
|
19
|
+
export const PROTOCOL_V1_ENVELOPE_HEADER_SIZE = 1 + 1 + PROTOCOL_V1_MESSAGE_HEADER_SIZE;
|
|
20
|
+
|
|
21
|
+
// ---- Protocol V2 (Pro2 USB / BLE transports) ----
|
|
22
|
+
|
|
23
|
+
/** Maximum V2 frame length, including header, 4000-byte payload, protobuf overhead, and CRC. */
|
|
24
|
+
export const PROTOCOL_V2_FRAME_MAX_BYTES = 4608;
|
|
25
|
+
|
|
26
|
+
/** FilesystemFileWrite chunk size over WebUSB. */
|
|
27
|
+
export const PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE = 4000;
|
|
28
|
+
|
|
29
|
+
/** FilesystemFileWrite chunk size over BLE. */
|
|
30
|
+
export const PROTOCOL_V2_BLE_FILE_CHUNK_SIZE = 1800;
|
|
31
|
+
|
|
32
|
+
/** BLE FilesystemFileRead chunk size, limited by the Pro2 1024-byte UART TX buffer. */
|
|
33
|
+
export const PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE = 900;
|
|
34
|
+
|
|
35
|
+
/** Pro2 BLE/UART RX FIFO must hold a complete Proto Link frame. */
|
|
36
|
+
export const PROTOCOL_V2_BLE_FRAME_MAX_BYTES = 2048;
|
|
37
|
+
|
|
38
|
+
/** @deprecated Use the transport-specific WebUSB or BLE file chunk constant. */
|
|
39
|
+
export const PROTOCOL_V2_FILE_CHUNK_SIZE = PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
40
|
+
|
|
5
41
|
/**
|
|
6
|
-
*
|
|
42
|
+
* Protocol V2 routing channel. USB reaches the main MCU directly, while BLE routes
|
|
43
|
+
* through the BLE coprocessor UART bridge.
|
|
7
44
|
*/
|
|
8
|
-
export const
|
|
45
|
+
export const PROTOCOL_V2_CHANNEL_USB = 0;
|
|
46
|
+
export const PROTOCOL_V2_CHANNEL_BLE_UART = 1;
|
|
47
|
+
export const PROTOCOL_V2_CHANNEL_SOCKET = 2;
|
|
48
|
+
|
|
49
|
+
/** packet_src for protobuf commands; firmware routes zero to the protobuf dispatcher. */
|
|
50
|
+
export const PROTOCOL_V2_PACKET_SRC_COMMAND = 0;
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import * as protobuf from 'protobufjs/light';
|
|
2
|
-
import
|
|
2
|
+
import Long from 'long';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
createMessageFromName,
|
|
6
|
+
createMessageFromType,
|
|
7
|
+
decodeProtobuf,
|
|
8
|
+
encodeProtobuf,
|
|
9
9
|
parseConfigure,
|
|
10
|
-
receiveOne,
|
|
11
10
|
} from './serialization';
|
|
11
|
+
import { PROTOCOL_V2_SYS_MESSAGE_THRESHOLD, ProtocolV1, ProtocolV2 } from './protocols';
|
|
12
|
+
import * as protocolV2Codec from './protocols/v2';
|
|
13
|
+
import { ProtocolV2LinkManager } from './protocols/v2/link-manager';
|
|
14
|
+
import { ProtocolV2UsbTransportBase } from './protocols/v2/usb-transport-base';
|
|
15
|
+
import {
|
|
16
|
+
ProtocolV2FrameAssembler,
|
|
17
|
+
ProtocolV2SequenceCursor,
|
|
18
|
+
ProtocolV2Session,
|
|
19
|
+
bytesToHex,
|
|
20
|
+
concatUint8Arrays,
|
|
21
|
+
getErrorMessage,
|
|
22
|
+
hexToBytes,
|
|
23
|
+
probeProtocolV2,
|
|
24
|
+
withProtocolTimeout,
|
|
25
|
+
} from './protocols/v2/session';
|
|
12
26
|
import * as check from './utils/highlevel-checks';
|
|
13
27
|
|
|
14
28
|
protobuf.util.Long = Long;
|
|
@@ -21,10 +35,12 @@ export type {
|
|
|
21
35
|
OneKeyMobileDeviceInfo,
|
|
22
36
|
OneKeyDeviceInfoWithSession,
|
|
23
37
|
MessageFromOneKey,
|
|
38
|
+
TransportCallOptions,
|
|
24
39
|
LowlevelTransportSharedPlugin,
|
|
25
40
|
LowLevelDevice,
|
|
26
41
|
OneKeyDeviceInfoBase,
|
|
27
42
|
OneKeyDeviceCommType,
|
|
43
|
+
ProtocolType,
|
|
28
44
|
} from './types';
|
|
29
45
|
|
|
30
46
|
export { Messages } from './types';
|
|
@@ -32,13 +48,33 @@ export * from './types/messages';
|
|
|
32
48
|
export * from './utils/logBlockCommand';
|
|
33
49
|
|
|
34
50
|
export * from './constants';
|
|
51
|
+
export * from './protocols';
|
|
52
|
+
export * as protocolV1 from './protocols/v1';
|
|
53
|
+
export * as protocolV2 from './protocols/v2';
|
|
54
|
+
export * from './protocols/v2/session';
|
|
55
|
+
export * from './protocols/v2/link-manager';
|
|
56
|
+
export * from './protocols/v2/usb-transport-base';
|
|
35
57
|
|
|
36
58
|
export default {
|
|
37
59
|
check,
|
|
38
|
-
buildOne,
|
|
39
|
-
buildBuffers,
|
|
40
|
-
buildEncodeBuffers,
|
|
41
|
-
receiveOne,
|
|
42
60
|
parseConfigure,
|
|
43
|
-
|
|
61
|
+
protocolV2: protocolV2Codec,
|
|
62
|
+
ProtocolV1,
|
|
63
|
+
ProtocolV2,
|
|
64
|
+
PROTOCOL_V2_SYS_MESSAGE_THRESHOLD,
|
|
65
|
+
ProtocolV2FrameAssembler,
|
|
66
|
+
ProtocolV2LinkManager,
|
|
67
|
+
ProtocolV2SequenceCursor,
|
|
68
|
+
ProtocolV2Session,
|
|
69
|
+
ProtocolV2UsbTransportBase,
|
|
70
|
+
bytesToHex,
|
|
71
|
+
concatUint8Arrays,
|
|
72
|
+
createMessageFromName,
|
|
73
|
+
createMessageFromType,
|
|
74
|
+
encodeProtobuf,
|
|
75
|
+
decodeProtobuf,
|
|
76
|
+
getErrorMessage,
|
|
77
|
+
hexToBytes,
|
|
78
|
+
probeProtocolV2,
|
|
79
|
+
withProtocolTimeout,
|
|
44
80
|
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import ByteBuffer from 'bytebuffer';
|
|
2
|
+
|
|
3
|
+
import { encodeEnvelopeMessage, encodeMessageChunks, encodeTransportPackets } from './v1/packets';
|
|
4
|
+
import { decodeFirstChunk } from './v1/decode';
|
|
5
|
+
import { decodeMessage as decodeV1Message } from './v1/receive';
|
|
6
|
+
import { createMessageFromName, createMessageFromType } from '../serialization/protobuf/messages';
|
|
7
|
+
import { encode as encodeProtobuf } from '../serialization/protobuf/encode';
|
|
8
|
+
import { decode as decodeProtobuf } from '../serialization/protobuf/decode';
|
|
9
|
+
import { decodeFrame as decodeV2Frame, encodeProtobufFrame, isAckFrame } from './v2';
|
|
10
|
+
|
|
11
|
+
import type { Root } from 'protobufjs/light';
|
|
12
|
+
|
|
13
|
+
export const PROTOCOL_V2_SYS_MESSAGE_THRESHOLD = 60000;
|
|
14
|
+
|
|
15
|
+
type ProtocolV2Schemas = {
|
|
16
|
+
protocolV1: Root;
|
|
17
|
+
protocolV2: Root;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type ProtocolV2FrameOptions = {
|
|
21
|
+
packetSrc?: number;
|
|
22
|
+
router?: number;
|
|
23
|
+
/** Sequence number (1-255). Managed per-session by ProtocolV2Session. */
|
|
24
|
+
seq?: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const resolveProtocolV2EncodeSchema = (name: string, schemas: ProtocolV2Schemas) => {
|
|
28
|
+
try {
|
|
29
|
+
schemas.protocolV2.lookupType(name);
|
|
30
|
+
return schemas.protocolV2;
|
|
31
|
+
} catch {
|
|
32
|
+
throw new Error(`Protocol V2 message "${name}" is not defined in Protocol V2 schema`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const PROTOCOL_V2_LEGACY_DECODE_ALLOWLIST = new Set([
|
|
37
|
+
'Failure',
|
|
38
|
+
'ButtonRequest',
|
|
39
|
+
'EntropyRequest',
|
|
40
|
+
'PinMatrixRequest',
|
|
41
|
+
'PassphraseRequest',
|
|
42
|
+
'Deprecated_PassphraseStateRequest',
|
|
43
|
+
'WordRequest',
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
const createProtocolV2MessageFromType = (messageTypeId: number, schemas: ProtocolV2Schemas) => {
|
|
47
|
+
try {
|
|
48
|
+
return createMessageFromType(schemas.protocolV2, messageTypeId);
|
|
49
|
+
} catch (protocolV2Error) {
|
|
50
|
+
let legacyMessage: ReturnType<typeof createMessageFromType>;
|
|
51
|
+
try {
|
|
52
|
+
legacyMessage = createMessageFromType(schemas.protocolV1, messageTypeId);
|
|
53
|
+
} catch {
|
|
54
|
+
throw protocolV2Error;
|
|
55
|
+
}
|
|
56
|
+
if (PROTOCOL_V2_LEGACY_DECODE_ALLOWLIST.has(legacyMessage.messageName)) {
|
|
57
|
+
return legacyMessage;
|
|
58
|
+
}
|
|
59
|
+
throw protocolV2Error;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const ProtocolV1 = {
|
|
64
|
+
encodeEnvelope: encodeEnvelopeMessage,
|
|
65
|
+
encodeMessageChunks,
|
|
66
|
+
encodeTransportPackets,
|
|
67
|
+
decodeFirstChunk,
|
|
68
|
+
|
|
69
|
+
decodeMessage: decodeV1Message,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const ProtocolV2 = {
|
|
73
|
+
isAckFrame,
|
|
74
|
+
|
|
75
|
+
inspectFrame(schemas: ProtocolV2Schemas, frame: Uint8Array) {
|
|
76
|
+
const { messageTypeId, pbPayload, seq } = decodeV2Frame(frame);
|
|
77
|
+
const { messageName } = createProtocolV2MessageFromType(messageTypeId, schemas);
|
|
78
|
+
return {
|
|
79
|
+
messageName,
|
|
80
|
+
messageTypeId,
|
|
81
|
+
pbPayload,
|
|
82
|
+
seq,
|
|
83
|
+
type: messageName,
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
encodeFrame(
|
|
88
|
+
schemas: ProtocolV2Schemas,
|
|
89
|
+
name: string,
|
|
90
|
+
data: Record<string, unknown>,
|
|
91
|
+
options: ProtocolV2FrameOptions = {}
|
|
92
|
+
) {
|
|
93
|
+
const encodeMessages = resolveProtocolV2EncodeSchema(name, schemas);
|
|
94
|
+
const { Message, messageTypeId } = createMessageFromName(encodeMessages, name);
|
|
95
|
+
const pbBuffer = encodeProtobuf(Message, data);
|
|
96
|
+
pbBuffer.reset();
|
|
97
|
+
const rawPbBuffer = pbBuffer.toBuffer() as unknown as ArrayBuffer;
|
|
98
|
+
const pbBytes = new Uint8Array(rawPbBuffer);
|
|
99
|
+
|
|
100
|
+
return encodeProtobufFrame(
|
|
101
|
+
messageTypeId,
|
|
102
|
+
pbBytes,
|
|
103
|
+
options.packetSrc,
|
|
104
|
+
options.router,
|
|
105
|
+
options.seq
|
|
106
|
+
);
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
decodeFrame(schemas: ProtocolV2Schemas, frame: Uint8Array) {
|
|
110
|
+
const { messageTypeId, pbPayload, seq, messageName } = this.inspectFrame(schemas, frame);
|
|
111
|
+
const { Message } = createProtocolV2MessageFromType(messageTypeId, schemas);
|
|
112
|
+
const rxByteBuffer = ByteBuffer.wrap(Buffer.from(pbPayload) as unknown as ArrayBuffer);
|
|
113
|
+
const message = decodeProtobuf(Message, rxByteBuffer);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
message,
|
|
117
|
+
messageName,
|
|
118
|
+
messageTypeId,
|
|
119
|
+
pbPayload,
|
|
120
|
+
seq,
|
|
121
|
+
type: messageName,
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ByteBuffer from 'bytebuffer';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { PROTOCOL_V1_HEADER_BYTE } from '../../constants';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Reads meta information from buffer
|
|
@@ -24,7 +24,7 @@ const readHeaderChunked = (buffer: ByteBuffer) => {
|
|
|
24
24
|
return { sharp1, sharp2, typeId, length };
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
export const
|
|
27
|
+
export const decodeEnvelope = (byteBuffer: ByteBuffer) => {
|
|
28
28
|
const { typeId } = readHeader(byteBuffer);
|
|
29
29
|
|
|
30
30
|
return {
|
|
@@ -35,13 +35,13 @@ export const decode = (byteBuffer: ByteBuffer) => {
|
|
|
35
35
|
|
|
36
36
|
// Parses first raw input that comes from Trezor and returns some information about the whole message.
|
|
37
37
|
// [compatibility]: accept Buffer just like decode does. But this would require changes in lower levels
|
|
38
|
-
export const
|
|
38
|
+
export const decodeFirstChunk = (bytes: ArrayBuffer) => {
|
|
39
39
|
// convert to ByteBuffer so it's easier to read
|
|
40
40
|
const byteBuffer = ByteBuffer.wrap(bytes, undefined, undefined, true);
|
|
41
41
|
|
|
42
42
|
const { sharp1, sharp2, typeId, length } = readHeaderChunked(byteBuffer);
|
|
43
43
|
|
|
44
|
-
if (sharp1 !==
|
|
44
|
+
if (sharp1 !== PROTOCOL_V1_HEADER_BYTE || sharp2 !== PROTOCOL_V1_HEADER_BYTE) {
|
|
45
45
|
throw new Error("Didn't receive expected header signature.");
|
|
46
46
|
}
|
|
47
47
|
|