@onekeyfe/hd-core 1.2.8-alpha.2 → 1.2.8-alpha.20
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 +27 -0
- package/__tests__/btc-derive-context-hash.test.ts +200 -0
- package/__tests__/device-lifecycle-events.test.ts +51 -7
- package/__tests__/device-pool-state.test.ts +50 -0
- package/__tests__/protocol-v2-bootloader-mode.test.ts +12 -0
- package/__tests__/usb-identity-probe.test.ts +293 -0
- package/dist/api/SearchDevices.d.ts +21 -1
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/btc/BTCDeriveContextHash.d.ts +16 -0
- package/dist/api/btc/BTCDeriveContextHash.d.ts.map +1 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +2 -0
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +45 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2788 -2522
- package/dist/inject.d.ts.map +1 -1
- package/dist/types/api/btcDeriveContextHash.d.ts +10 -0
- package/dist/types/api/btcDeriveContextHash.d.ts.map +1 -0
- package/dist/types/api/export.d.ts +1 -0
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +2 -0
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/device.d.ts +8 -0
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/SearchDevices.ts +163 -7
- package/src/api/btc/BTCDeriveContextHash.ts +90 -0
- package/src/api/index.ts +1 -0
- package/src/api/protocol-v2/DeviceReboot.ts +2 -0
- package/src/core/index.ts +23 -7
- package/src/data/messages/messages.json +51 -0
- package/src/device/Device.ts +10 -0
- package/src/device/DevicePool.ts +12 -2
- package/src/events/logBlockEvent.ts +1 -0
- package/src/index.ts +1 -0
- package/src/inject.ts +2 -0
- package/src/types/api/btcDeriveContextHash.ts +15 -0
- package/src/types/api/export.ts +1 -0
- package/src/types/api/index.ts +2 -0
- package/src/types/device.ts +10 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "1.2.8-alpha.
|
|
3
|
+
"version": "1.2.8-alpha.20",
|
|
4
4
|
"description": "Core processes and APIs for communicating with OneKey hardware devices.",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@onekeyfe/hd-shared": "1.2.8-alpha.
|
|
29
|
-
"@onekeyfe/hd-transport": "1.2.8-alpha.
|
|
28
|
+
"@onekeyfe/hd-shared": "1.2.8-alpha.20",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.2.8-alpha.20",
|
|
30
30
|
"axios": "1.15.2",
|
|
31
31
|
"bignumber.js": "^9.0.2",
|
|
32
32
|
"buffer": "^6.0.3",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"@types/w3c-web-usb": "^1.0.10",
|
|
47
47
|
"@types/web-bluetooth": "^0.0.21"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "973f059fe93a58fdcf098ddb32ba63f621a2c78e"
|
|
50
50
|
}
|
package/src/api/SearchDevices.ts
CHANGED
|
@@ -5,10 +5,20 @@ import TransportManager from '../data-manager/TransportManager';
|
|
|
5
5
|
import { DataManager } from '../data-manager';
|
|
6
6
|
import { LoggerNames, getDeviceTypeByBleName, getLogger } from '../utils';
|
|
7
7
|
import { DevicePool } from '../device/DevicePool';
|
|
8
|
+
import { buildProtocolV1FeaturesPayload } from '../deviceProfile/buildDeviceFeatures';
|
|
9
|
+
import { mapProtocolV2DeviceInfoToState } from '../device/DeviceStateMapper';
|
|
10
|
+
import { getSupportProtocolV1MessageSchema } from '../utils/deviceFeaturesUtils';
|
|
11
|
+
import { PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST } from '../protocols/protocol-v2/features';
|
|
8
12
|
|
|
9
|
-
import type { SearchDevice } from '../types/device';
|
|
13
|
+
import type { SearchDevice, UsbDeviceIdentity } from '../types/device';
|
|
14
|
+
import type { PROTO } from '../constants';
|
|
10
15
|
import type DeviceConnector from '../device/DeviceConnector';
|
|
11
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
AcquireInput,
|
|
18
|
+
OneKeyDeviceInfo as DeviceDescriptor,
|
|
19
|
+
ProtocolV2DeviceInfo,
|
|
20
|
+
Transport,
|
|
21
|
+
} from '@onekeyfe/hd-transport';
|
|
12
22
|
import type { Device } from '../device/Device';
|
|
13
23
|
|
|
14
24
|
const Log = getLogger(LoggerNames.DevicePool);
|
|
@@ -18,7 +28,10 @@ type RequestQueueLookup = {
|
|
|
18
28
|
getTask: (requestId: number) => { method: { device?: Device } } | undefined;
|
|
19
29
|
};
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
export type UsbIdentityProbeDescriptor = Pick<DeviceDescriptor, 'path' | 'commType'> &
|
|
32
|
+
Partial<Pick<DeviceDescriptor, 'id' | 'session'>>;
|
|
33
|
+
|
|
34
|
+
const getDescriptorKeys = (descriptor: UsbIdentityProbeDescriptor) =>
|
|
22
35
|
[descriptor.path, descriptor.id].filter(
|
|
23
36
|
(key): key is string => typeof key === 'string' && key.length > 0
|
|
24
37
|
);
|
|
@@ -33,11 +46,11 @@ const isOwnedByActiveWebUsbRequest = (
|
|
|
33
46
|
);
|
|
34
47
|
};
|
|
35
48
|
|
|
36
|
-
const getUsbHandle = (descriptor?:
|
|
49
|
+
const getUsbHandle = (descriptor?: UsbIdentityProbeDescriptor) =>
|
|
37
50
|
(descriptor as { device?: unknown } | undefined)?.device;
|
|
38
51
|
|
|
39
52
|
const getOwningRequestDevice = (
|
|
40
|
-
descriptor:
|
|
53
|
+
descriptor: UsbIdentityProbeDescriptor,
|
|
41
54
|
requestQueue?: RequestQueueLookup
|
|
42
55
|
) => {
|
|
43
56
|
if (!requestQueue) return undefined;
|
|
@@ -60,6 +73,149 @@ const getOwningRequestDevice = (
|
|
|
60
73
|
return undefined;
|
|
61
74
|
};
|
|
62
75
|
|
|
76
|
+
export const getOwnedUsbDeviceIdentity = (
|
|
77
|
+
descriptor: UsbIdentityProbeDescriptor,
|
|
78
|
+
requestQueue: RequestQueueLookup
|
|
79
|
+
): UsbDeviceIdentity | undefined => {
|
|
80
|
+
const owner = getOwningRequestDevice(descriptor, requestQueue);
|
|
81
|
+
if (!owner || descriptor.commType !== 'webusb' || owner.originalDescriptor?.commType !== 'webusb')
|
|
82
|
+
return undefined;
|
|
83
|
+
return {
|
|
84
|
+
path: descriptor.path,
|
|
85
|
+
serialNo: owner.getCurrentSerialNo() || null,
|
|
86
|
+
deviceId: owner.getCurrentDeviceId() || null,
|
|
87
|
+
deviceType: owner.getCurrentDeviceType(),
|
|
88
|
+
connectProtocol: owner.getProtocol(),
|
|
89
|
+
commType: 'webusb',
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export type UsbIdentityProbeTransport = Pick<
|
|
94
|
+
Transport,
|
|
95
|
+
'release' | 'call' | 'getProtocolType' | 'configureProtocolV2'
|
|
96
|
+
> & {
|
|
97
|
+
init: (logger: object) => unknown;
|
|
98
|
+
enumerate: () => Promise<UsbIdentityProbeDescriptor[]>;
|
|
99
|
+
acquire: (input: AcquireInput) => Promise<string | undefined>;
|
|
100
|
+
configure: (signedData: string) => void | Promise<void>;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Identity discovery must not publish device events or log raw Features/session data.
|
|
104
|
+
const identityProbeLogger = {
|
|
105
|
+
debug: () => undefined,
|
|
106
|
+
log: () => undefined,
|
|
107
|
+
info: () => undefined,
|
|
108
|
+
warn: () => undefined,
|
|
109
|
+
error: () => undefined,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/** Runs on a separate transport; never initializes or switches the current Core. */
|
|
113
|
+
export const probeUsbDeviceIdentitiesWithTransport = async (
|
|
114
|
+
transport: UsbIdentityProbeTransport,
|
|
115
|
+
commType: UsbDeviceIdentity['commType'],
|
|
116
|
+
getOwnedIdentity?: (descriptor: UsbIdentityProbeDescriptor) => UsbDeviceIdentity | undefined
|
|
117
|
+
): Promise<UsbDeviceIdentity[]> => {
|
|
118
|
+
if (commType !== 'webusb') return [];
|
|
119
|
+
await transport.init(identityProbeLogger);
|
|
120
|
+
const defaultMessages = JSON.stringify(DataManager.getProtobufMessages());
|
|
121
|
+
await transport.configure(defaultMessages);
|
|
122
|
+
await transport.configureProtocolV2?.(
|
|
123
|
+
JSON.stringify(DataManager.getProtobufMessages('v2Schema'))
|
|
124
|
+
);
|
|
125
|
+
const descriptors = await transport.enumerate();
|
|
126
|
+
const identities: UsbDeviceIdentity[] = [];
|
|
127
|
+
const readIdentity = async (
|
|
128
|
+
descriptor: UsbIdentityProbeDescriptor
|
|
129
|
+
): Promise<UsbDeviceIdentity | undefined> => {
|
|
130
|
+
const { path } = descriptor;
|
|
131
|
+
if (!path || descriptor.commType !== commType) return undefined;
|
|
132
|
+
if (descriptors.filter(candidate => candidate.path === path).length !== 1) return undefined;
|
|
133
|
+
const owner = getOwnedIdentity?.(descriptor);
|
|
134
|
+
if (owner) {
|
|
135
|
+
return owner;
|
|
136
|
+
}
|
|
137
|
+
// Do not borrow a browser handle or replace a Bridge session owned by another call.
|
|
138
|
+
if (descriptor.session || (getUsbHandle(descriptor) as USBDevice | undefined)?.opened)
|
|
139
|
+
return undefined;
|
|
140
|
+
|
|
141
|
+
let session: string | undefined;
|
|
142
|
+
let identified: UsbDeviceIdentity | undefined;
|
|
143
|
+
try {
|
|
144
|
+
await transport.configure(defaultMessages);
|
|
145
|
+
session = await transport.acquire({ path, previous: null, forceProtocolDetection: true });
|
|
146
|
+
if (!session) return undefined;
|
|
147
|
+
const protocol = transport.getProtocolType(path);
|
|
148
|
+
if (protocol === 'V1') {
|
|
149
|
+
const response = await transport.call(session, 'GetFeatures', {}, { timeoutMs: 3000 });
|
|
150
|
+
if (response.type !== 'Features') return undefined;
|
|
151
|
+
let features = buildProtocolV1FeaturesPayload(response.message as PROTO.Features);
|
|
152
|
+
if (!features.serialNo) {
|
|
153
|
+
const { messages, protocolV1MessageSchema } = getSupportProtocolV1MessageSchema(features);
|
|
154
|
+
// Legacy V1 has no OnekeyGetFeatures; keep the identity confirmed by GetFeatures.
|
|
155
|
+
if (protocolV1MessageSchema === 'v1CurrentSchema') {
|
|
156
|
+
if (messages) await transport.configure(JSON.stringify(messages));
|
|
157
|
+
const vendor = await transport.call(
|
|
158
|
+
session,
|
|
159
|
+
'OnekeyGetFeatures',
|
|
160
|
+
{},
|
|
161
|
+
{ timeoutMs: 3000 }
|
|
162
|
+
);
|
|
163
|
+
if (vendor.type === 'OnekeyFeatures') {
|
|
164
|
+
features = buildProtocolV1FeaturesPayload({
|
|
165
|
+
...response.message,
|
|
166
|
+
...vendor.message,
|
|
167
|
+
} as PROTO.Features);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
identified = {
|
|
172
|
+
path,
|
|
173
|
+
serialNo: features.serialNo || null,
|
|
174
|
+
deviceId: features.deviceId || null,
|
|
175
|
+
deviceType: features.deviceType,
|
|
176
|
+
connectProtocol: 'V1',
|
|
177
|
+
commType,
|
|
178
|
+
};
|
|
179
|
+
} else if (protocol === 'V2') {
|
|
180
|
+
const response = await transport.call(
|
|
181
|
+
session,
|
|
182
|
+
'DeviceInfoGet',
|
|
183
|
+
PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST,
|
|
184
|
+
{ timeoutMs: 3000 }
|
|
185
|
+
);
|
|
186
|
+
if (response.type !== 'DeviceInfo') return undefined;
|
|
187
|
+
const { identity } = mapProtocolV2DeviceInfoToState(
|
|
188
|
+
response.message as ProtocolV2DeviceInfo
|
|
189
|
+
);
|
|
190
|
+
identified = {
|
|
191
|
+
path,
|
|
192
|
+
serialNo: identity?.serialNo || null,
|
|
193
|
+
deviceId: identity?.deviceId || null,
|
|
194
|
+
deviceType: identity?.deviceType ?? EDeviceType.Unknown,
|
|
195
|
+
connectProtocol: 'V2',
|
|
196
|
+
commType,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
// Discovery is best effort; a busy/disconnected device cannot confirm identity.
|
|
201
|
+
} finally {
|
|
202
|
+
if (session) {
|
|
203
|
+
try {
|
|
204
|
+
await transport.release(session, false);
|
|
205
|
+
} catch {
|
|
206
|
+
identified = undefined;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return identified;
|
|
211
|
+
};
|
|
212
|
+
for (const descriptor of descriptors) {
|
|
213
|
+
const identity = await readIdentity(descriptor);
|
|
214
|
+
if (identity) identities.push(identity);
|
|
215
|
+
}
|
|
216
|
+
return identities;
|
|
217
|
+
};
|
|
218
|
+
|
|
63
219
|
// A USB path is a routing key (the USB serial, a synthesized usb-vid-pid-name, or the
|
|
64
220
|
// bootloader placeholder), not a hardware identity, so it never fills uuid/serialNo.
|
|
65
221
|
const toSearchDeviceFromDescriptor = (descriptor: DeviceDescriptor): SearchDevice => {
|
|
@@ -81,7 +237,7 @@ const toSearchDevice = (device: Device): SearchDevice | null => {
|
|
|
81
237
|
};
|
|
82
238
|
|
|
83
239
|
export default class SearchDevices extends BaseMethod {
|
|
84
|
-
connector?: DeviceConnector;
|
|
240
|
+
connector?: DeviceConnector = undefined;
|
|
85
241
|
|
|
86
242
|
init() {
|
|
87
243
|
this.useDevice = false;
|
|
@@ -130,7 +286,7 @@ export default class SearchDevices extends BaseMethod {
|
|
|
130
286
|
// BLE discovery cannot provide a physical-device identity before initialization.
|
|
131
287
|
uuid: '',
|
|
132
288
|
deviceId: null,
|
|
133
|
-
name: bleName || device.name,
|
|
289
|
+
name: bleName || device.name || '',
|
|
134
290
|
deviceType: getDeviceTypeByBleName(bleName),
|
|
135
291
|
});
|
|
136
292
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { Buffer } from 'buffer';
|
|
3
|
+
|
|
4
|
+
import { BaseMethod } from '../BaseMethod';
|
|
5
|
+
import { invalidParameter, validateParams } from '../helpers/paramsValidator';
|
|
6
|
+
import { getScriptType, validatePath } from '../helpers/pathUtils';
|
|
7
|
+
|
|
8
|
+
import type { BabylonDeriveContextHash, CanonicalBitcoinNetwork } from '@onekeyfe/hd-transport';
|
|
9
|
+
import type { BTCDeriveContextHashParams } from '../../types';
|
|
10
|
+
|
|
11
|
+
const NETWORKS: Record<BTCDeriveContextHashParams['network'], CanonicalBitcoinNetwork> = {
|
|
12
|
+
'bitcoin-mainnet': 'BITCOIN_MAINNET',
|
|
13
|
+
'bitcoin-testnet': 'BITCOIN_TESTNET',
|
|
14
|
+
'bitcoin-signet': 'BITCOIN_SIGNET',
|
|
15
|
+
'bitcoin-regtest': 'BITCOIN_REGTEST',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default class BTCDeriveContextHash extends BaseMethod<BabylonDeriveContextHash> {
|
|
19
|
+
strictCheckDeviceSupport = true;
|
|
20
|
+
|
|
21
|
+
init() {
|
|
22
|
+
this.checkDeviceId = true;
|
|
23
|
+
this.allowUsePreInitialize = true;
|
|
24
|
+
|
|
25
|
+
validateParams(this.payload, [
|
|
26
|
+
{ name: 'path', required: true },
|
|
27
|
+
{ name: 'appName', type: 'string', required: true },
|
|
28
|
+
{ name: 'context', type: 'string', required: true },
|
|
29
|
+
{ name: 'network', type: 'string', required: true },
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const { path, appName, context, network } = this.payload;
|
|
33
|
+
if (typeof appName !== 'string' || !/^[a-z0-9-]{1,64}$/.test(appName)) {
|
|
34
|
+
throw invalidParameter(
|
|
35
|
+
'appName must contain 1..64 ASCII lowercase letters, digits or hyphens'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
if (typeof context !== 'string' || !/^(?:[0-9a-f]{2}){1,1024}$/.test(context)) {
|
|
39
|
+
throw invalidParameter('context must be 1..1024 bytes of lowercase hex without a prefix');
|
|
40
|
+
}
|
|
41
|
+
if (typeof network !== 'string' || !Object.prototype.hasOwnProperty.call(NETWORKS, network)) {
|
|
42
|
+
throw invalidParameter('Unsupported Bitcoin network');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Reject malformed or overflowing indices before validatePath can truncate them.
|
|
46
|
+
if (
|
|
47
|
+
(typeof path === 'string' &&
|
|
48
|
+
(!/^m(?:\/\d+'?)+$/i.test(path) ||
|
|
49
|
+
path
|
|
50
|
+
.split('/')
|
|
51
|
+
.slice(1)
|
|
52
|
+
.some(index => Number(index.replace(/'/g, '')) >= 0x80000000))) ||
|
|
53
|
+
(Array.isArray(path) &&
|
|
54
|
+
path.findIndex(index => !Number.isInteger(index) || index < 0 || index > 0xffffffff) !== -1)
|
|
55
|
+
) {
|
|
56
|
+
throw invalidParameter('Not a valid BIP-32 path');
|
|
57
|
+
}
|
|
58
|
+
const addressN = validatePath(path, 3);
|
|
59
|
+
|
|
60
|
+
this.params = {
|
|
61
|
+
address_n: addressN,
|
|
62
|
+
script_type: getScriptType(addressN),
|
|
63
|
+
app_name: Buffer.from(appName, 'ascii').toString('hex'),
|
|
64
|
+
context,
|
|
65
|
+
network: NETWORKS[network as BTCDeriveContextHashParams['network']],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getVersionRange() {
|
|
70
|
+
return {
|
|
71
|
+
classic1s: { min: '3.21.0' },
|
|
72
|
+
classicpure: { min: '3.21.0' },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async run() {
|
|
77
|
+
const { message } = await this.device.commands.typedCall(
|
|
78
|
+
'BabylonDeriveContextHash',
|
|
79
|
+
'BabylonDerivedContextHash',
|
|
80
|
+
this.params
|
|
81
|
+
);
|
|
82
|
+
if (typeof message.secret !== 'string' || !/^[0-9a-f]{64}$/.test(message.secret)) {
|
|
83
|
+
throw ERRORS.TypedError(
|
|
84
|
+
HardwareErrorCode.CallMethodError,
|
|
85
|
+
'Invalid Babylon context hash response'
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return message;
|
|
89
|
+
}
|
|
90
|
+
}
|
package/src/api/index.ts
CHANGED
|
@@ -60,6 +60,7 @@ export { default as allNetworkGetAddressByLoop } from './allnetwork/AllNetworkGe
|
|
|
60
60
|
|
|
61
61
|
export { default as btcGetAddress } from './btc/BTCGetAddress';
|
|
62
62
|
export { default as btcGetPublicKey } from './btc/BTCGetPublicKey';
|
|
63
|
+
export { default as btcDeriveContextHash } from './btc/BTCDeriveContextHash';
|
|
63
64
|
export { default as btcSignMessage } from './btc/BTCSignMessage';
|
|
64
65
|
export { default as btcSignPsbt } from './btc/BTCSignPsbt';
|
|
65
66
|
export { default as btcSignTransaction } from './btc/BTCSignTransaction';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
2
2
|
|
|
3
|
+
import { UI_REQUEST } from '../../constants/ui-request';
|
|
3
4
|
import { BaseMethod } from '../BaseMethod';
|
|
4
5
|
import { normalizeRebootType } from './helpers';
|
|
5
6
|
|
|
@@ -11,6 +12,7 @@ export default class DeviceReboot extends BaseMethod<DeviceRebootParams> {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
init() {
|
|
15
|
+
this.allowDeviceMode.push(UI_REQUEST.BOOTLOADER);
|
|
14
16
|
this.skipForceUpdateCheck = true;
|
|
15
17
|
this.useDevicePassphraseState = false;
|
|
16
18
|
this.unlockPolicy = 'unlock-before-run';
|
package/src/core/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import semver from 'semver';
|
|
2
|
+
import { getOwnedUsbDeviceIdentity } from '../api/SearchDevices';
|
|
2
3
|
import EventEmitter from 'events';
|
|
3
4
|
import {
|
|
4
5
|
DeviceSessionPinType,
|
|
@@ -1589,7 +1590,7 @@ const onDeviceConnectHandler = (device: Device) => {
|
|
|
1589
1590
|
|
|
1590
1591
|
const onDeviceDisconnectHandler = (device: Device) => {
|
|
1591
1592
|
device.clearPreInitialized();
|
|
1592
|
-
const deviceObject = device.
|
|
1593
|
+
const deviceObject = device.toDisconnectMessageObject();
|
|
1593
1594
|
if (!deviceObject) return;
|
|
1594
1595
|
postMessage(createDeviceMessage(DEVICE.DISCONNECT, { device: deviceObject }));
|
|
1595
1596
|
};
|
|
@@ -1826,6 +1827,10 @@ export default class Core extends EventEmitter {
|
|
|
1826
1827
|
|
|
1827
1828
|
private requestQueue = new RequestQueue();
|
|
1828
1829
|
|
|
1830
|
+
getOwnedUsbDeviceIdentity(descriptor: Parameters<typeof getOwnedUsbDeviceIdentity>[0]) {
|
|
1831
|
+
return getOwnedUsbDeviceIdentity(descriptor, this.requestQueue);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1829
1834
|
private disposePromise?: Promise<void>;
|
|
1830
1835
|
|
|
1831
1836
|
// background task
|
|
@@ -2038,6 +2043,8 @@ export const init = async (
|
|
|
2038
2043
|
}
|
|
2039
2044
|
};
|
|
2040
2045
|
|
|
2046
|
+
let pendingTransportSwitch: Promise<void> = Promise.resolve();
|
|
2047
|
+
|
|
2041
2048
|
export const switchTransport = ({
|
|
2042
2049
|
env,
|
|
2043
2050
|
Transport,
|
|
@@ -2047,10 +2054,19 @@ export const switchTransport = ({
|
|
|
2047
2054
|
Transport: any;
|
|
2048
2055
|
plugin?: LowlevelTransportSharedPlugin;
|
|
2049
2056
|
}) => {
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2057
|
+
// Preserve request order while native transport cleanup is pending.
|
|
2058
|
+
const switching = pendingTransportSwitch
|
|
2059
|
+
.catch(() => undefined)
|
|
2060
|
+
.then(async () => {
|
|
2061
|
+
_connector?.stop();
|
|
2062
|
+
await TransportManager.getTransport()?.stop?.();
|
|
2063
|
+
DataManager.updateEnv(env);
|
|
2064
|
+
TransportManager.setTransport(Transport, plugin);
|
|
2065
|
+
_deviceList = undefined;
|
|
2066
|
+
DevicePool.resetState();
|
|
2067
|
+
_connector = undefined;
|
|
2068
|
+
initConnector();
|
|
2069
|
+
});
|
|
2070
|
+
pendingTransportSwitch = switching;
|
|
2071
|
+
return switching;
|
|
2056
2072
|
};
|
|
@@ -12485,6 +12485,8 @@
|
|
|
12485
12485
|
"(has_bitcoin_only_values)": true
|
|
12486
12486
|
},
|
|
12487
12487
|
"values": {
|
|
12488
|
+
"MessageType_BabylonDeriveContextHash": 10054,
|
|
12489
|
+
"MessageType_BabylonDerivedContextHash": 10055,
|
|
12488
12490
|
"MessageType_Initialize": 0,
|
|
12489
12491
|
"MessageType_Ping": 1,
|
|
12490
12492
|
"MessageType_Success": 2,
|
|
@@ -12949,6 +12951,55 @@
|
|
|
12949
12951
|
"MessageType_UpgradeFileHeader": 10050
|
|
12950
12952
|
}
|
|
12951
12953
|
},
|
|
12954
|
+
"CanonicalBitcoinNetwork": {
|
|
12955
|
+
"values": {
|
|
12956
|
+
"BITCOIN_MAINNET": 0,
|
|
12957
|
+
"BITCOIN_TESTNET": 1,
|
|
12958
|
+
"BITCOIN_SIGNET": 2,
|
|
12959
|
+
"BITCOIN_REGTEST": 3
|
|
12960
|
+
}
|
|
12961
|
+
},
|
|
12962
|
+
"BabylonDeriveContextHash": {
|
|
12963
|
+
"fields": {
|
|
12964
|
+
"address_n": {
|
|
12965
|
+
"rule": "repeated",
|
|
12966
|
+
"type": "uint32",
|
|
12967
|
+
"id": 1,
|
|
12968
|
+
"options": {
|
|
12969
|
+
"packed": false
|
|
12970
|
+
}
|
|
12971
|
+
},
|
|
12972
|
+
"script_type": {
|
|
12973
|
+
"rule": "required",
|
|
12974
|
+
"type": "InputScriptType",
|
|
12975
|
+
"id": 2
|
|
12976
|
+
},
|
|
12977
|
+
"app_name": {
|
|
12978
|
+
"rule": "required",
|
|
12979
|
+
"type": "bytes",
|
|
12980
|
+
"id": 3
|
|
12981
|
+
},
|
|
12982
|
+
"context": {
|
|
12983
|
+
"rule": "required",
|
|
12984
|
+
"type": "bytes",
|
|
12985
|
+
"id": 4
|
|
12986
|
+
},
|
|
12987
|
+
"network": {
|
|
12988
|
+
"rule": "required",
|
|
12989
|
+
"type": "CanonicalBitcoinNetwork",
|
|
12990
|
+
"id": 5
|
|
12991
|
+
}
|
|
12992
|
+
}
|
|
12993
|
+
},
|
|
12994
|
+
"BabylonDerivedContextHash": {
|
|
12995
|
+
"fields": {
|
|
12996
|
+
"secret": {
|
|
12997
|
+
"rule": "required",
|
|
12998
|
+
"type": "bytes",
|
|
12999
|
+
"id": 1
|
|
13000
|
+
}
|
|
13001
|
+
}
|
|
13002
|
+
},
|
|
12952
13003
|
"google": {
|
|
12953
13004
|
"nested": {
|
|
12954
13005
|
"protobuf": {
|
package/src/device/Device.ts
CHANGED
|
@@ -248,6 +248,9 @@ export class Device extends EventEmitter {
|
|
|
248
248
|
/** Canonical device-state cache; legacy Features is a compatibility projection. */
|
|
249
249
|
private stateStore = new DeviceStateStore();
|
|
250
250
|
|
|
251
|
+
/** Retain the old public identity only for disconnect notifications after wipe. */
|
|
252
|
+
private deviceBeforeWipe: DeviceTyped | null = null;
|
|
253
|
+
|
|
251
254
|
/** Force the next initialization to reload DeviceInfo after reconnect or reboot. */
|
|
252
255
|
private protocolV2StateNeedsReload = false;
|
|
253
256
|
|
|
@@ -351,6 +354,8 @@ export class Device extends EventEmitter {
|
|
|
351
354
|
toMessageObject(): DeviceTyped | null {
|
|
352
355
|
if (this.isUnacquired() || !this.features) return null;
|
|
353
356
|
|
|
357
|
+
this.deviceBeforeWipe = null;
|
|
358
|
+
|
|
354
359
|
const env = DataManager.getSettings('env');
|
|
355
360
|
const deviceType = this.getCurrentDeviceType();
|
|
356
361
|
|
|
@@ -395,6 +400,10 @@ export class Device extends EventEmitter {
|
|
|
395
400
|
};
|
|
396
401
|
}
|
|
397
402
|
|
|
403
|
+
toDisconnectMessageObject(): DeviceTyped | null {
|
|
404
|
+
return this.toMessageObject() ?? this.deviceBeforeWipe;
|
|
405
|
+
}
|
|
406
|
+
|
|
398
407
|
/**
|
|
399
408
|
* Device connect
|
|
400
409
|
* @returns {Promise<boolean>}
|
|
@@ -1364,6 +1373,7 @@ export class Device extends EventEmitter {
|
|
|
1364
1373
|
}
|
|
1365
1374
|
|
|
1366
1375
|
invalidateAfterWipe() {
|
|
1376
|
+
this.deviceBeforeWipe = this.toMessageObject() ?? this.deviceBeforeWipe;
|
|
1367
1377
|
const deviceId = this.getCurrentDeviceId();
|
|
1368
1378
|
if (deviceId) {
|
|
1369
1379
|
deviceWalletSessionStore.deleteDevice(deviceId);
|
package/src/device/DevicePool.ts
CHANGED
|
@@ -114,7 +114,11 @@ export class DevicePool extends EventEmitter {
|
|
|
114
114
|
const exist = canPathIdentifyDevice(cachedPath)
|
|
115
115
|
? descriptorList.find(d => d.path === cachedPath)
|
|
116
116
|
: undefined;
|
|
117
|
-
if (
|
|
117
|
+
if (
|
|
118
|
+
exist &&
|
|
119
|
+
!initOptions?.forceProtocolDetection &&
|
|
120
|
+
!(device.needReloadDevice && device.isUnacquired())
|
|
121
|
+
) {
|
|
118
122
|
// Log.debug('find existed Device: ', connectId);
|
|
119
123
|
device.updateDescriptor(exist, true);
|
|
120
124
|
await this._refreshRuntimeState(device, initOptions);
|
|
@@ -170,7 +174,13 @@ export class DevicePool extends EventEmitter {
|
|
|
170
174
|
const forceProtocolDetection = initOptions?.forceProtocolDetection === true;
|
|
171
175
|
const isNewDevice = !cachedDevice;
|
|
172
176
|
const device = cachedDevice ?? Device.fromDescriptor(descriptor);
|
|
173
|
-
if (
|
|
177
|
+
if (
|
|
178
|
+
isNewDevice ||
|
|
179
|
+
forceProtocolDetection ||
|
|
180
|
+
(device.needReloadDevice && device.isUnacquired())
|
|
181
|
+
) {
|
|
182
|
+
// Wipe invalidates state while the same USB path can remain discoverable.
|
|
183
|
+
if (cachedDevice) device.updateDescriptor(descriptor, true);
|
|
174
184
|
device.deviceConnector = this.connector;
|
|
175
185
|
// Search/discovery acquires outside Device.run(). Start a new attempt so
|
|
176
186
|
// a previous user cancel does not hide this device from later searches.
|
|
@@ -11,6 +11,7 @@ export const LogBlockEvent: Set<string> = new Set([
|
|
|
11
11
|
// These resource/file APIs did not exist in 1.1.32. Skip the payload so
|
|
12
12
|
// the log layer does not copy huge Base64 or binary data.
|
|
13
13
|
const LogPayloadBlockMethod: Set<string> = new Set([
|
|
14
|
+
'btcDeriveContextHash',
|
|
14
15
|
'deviceUploadNft',
|
|
15
16
|
'deviceUploadWallpaper',
|
|
16
17
|
'uploadPortfolio',
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export { executeCallback, cleanupCallback };
|
|
|
21
21
|
export { preloadSessionCache } from './device/Device';
|
|
22
22
|
export { projectFeatures as projectDeviceStateFeatures } from './device/DeviceStateProjector';
|
|
23
23
|
export { getMethodSupportedProtocols } from './api/utils';
|
|
24
|
+
export { probeUsbDeviceIdentitiesWithTransport } from './api/SearchDevices';
|
|
24
25
|
export {
|
|
25
26
|
getFirmwareUpdateHostBindingGeneration,
|
|
26
27
|
registerFirmwareUpdateHostBinding,
|
package/src/inject.ts
CHANGED
|
@@ -309,6 +309,8 @@ export const createCoreApi = (
|
|
|
309
309
|
call({ ...params, connectId, deviceId, method: 'btcGetAddress' }),
|
|
310
310
|
btcGetPublicKey: (connectId, deviceId, params) =>
|
|
311
311
|
call({ ...params, connectId, deviceId, method: 'btcGetPublicKey' }),
|
|
312
|
+
btcDeriveContextHash: (connectId, deviceId, params) =>
|
|
313
|
+
call({ ...params, connectId, deviceId, method: 'btcDeriveContextHash' }),
|
|
312
314
|
btcSignMessage: (connectId, deviceId, params) =>
|
|
313
315
|
call({ ...params, connectId, deviceId, method: 'btcSignMessage' }),
|
|
314
316
|
btcSignPsbt: (connectId, deviceId, params) =>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BabylonDerivedContextHash } from '@onekeyfe/hd-transport';
|
|
2
|
+
import type { CommonParams, Response } from '../params';
|
|
3
|
+
|
|
4
|
+
export type BTCDeriveContextHashParams = {
|
|
5
|
+
path: string | number[];
|
|
6
|
+
appName: string;
|
|
7
|
+
context: string;
|
|
8
|
+
network: 'bitcoin-mainnet' | 'bitcoin-testnet' | 'bitcoin-signet' | 'bitcoin-regtest';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export declare function btcDeriveContextHash(
|
|
12
|
+
connectId: string,
|
|
13
|
+
deviceId: string,
|
|
14
|
+
params: CommonParams & BTCDeriveContextHashParams
|
|
15
|
+
): Response<BabylonDerivedContextHash>;
|
package/src/types/api/export.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type { BTCAddress, BTCGetAddressParams } from './btcGetAddress';
|
|
2
2
|
export type { BTCPublicKey, BTCGetPublicKeyParams } from './btcGetPublicKey';
|
|
3
|
+
export type { BTCDeriveContextHashParams } from './btcDeriveContextHash';
|
|
3
4
|
export type { BTCSignMessageParams } from './btcSignMessage';
|
|
4
5
|
export type { BTCVerifyMessageParams } from './btcVerifyMessage';
|
|
5
6
|
export type {
|
package/src/types/api/index.ts
CHANGED
|
@@ -73,6 +73,7 @@ import type { evmSignTypedData } from './evmSignTypedData';
|
|
|
73
73
|
import type { evmVerifyMessage } from './evmVerifyMessage';
|
|
74
74
|
import type { btcGetAddress } from './btcGetAddress';
|
|
75
75
|
import type { btcGetPublicKey } from './btcGetPublicKey';
|
|
76
|
+
import type { btcDeriveContextHash } from './btcDeriveContextHash';
|
|
76
77
|
import type { btcSignMessage } from './btcSignMessage';
|
|
77
78
|
import type { btcSignPsbt } from './btcSignPsbt';
|
|
78
79
|
import type { btcSignTransaction } from './btcSignTransaction';
|
|
@@ -286,6 +287,7 @@ export type CoreApi = {
|
|
|
286
287
|
*/
|
|
287
288
|
btcGetAddress: typeof btcGetAddress;
|
|
288
289
|
btcGetPublicKey: typeof btcGetPublicKey;
|
|
290
|
+
btcDeriveContextHash: typeof btcDeriveContextHash;
|
|
289
291
|
btcSignMessage: typeof btcSignMessage;
|
|
290
292
|
btcSignPsbt: typeof btcSignPsbt;
|
|
291
293
|
btcSignTransaction: typeof btcSignTransaction;
|
package/src/types/device.ts
CHANGED
|
@@ -85,6 +85,16 @@ export type SearchDevice = {
|
|
|
85
85
|
commType: OneKeyDeviceCommType;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
+
/** Identity confirmed over USB. The descriptor path is only a transport address. */
|
|
89
|
+
export type UsbDeviceIdentity = {
|
|
90
|
+
path: string;
|
|
91
|
+
serialNo: string | null;
|
|
92
|
+
deviceId: string | null;
|
|
93
|
+
deviceType: IDeviceType;
|
|
94
|
+
connectProtocol: HardwareConnectProtocol;
|
|
95
|
+
commType: 'webusb' | 'bridge';
|
|
96
|
+
};
|
|
97
|
+
|
|
88
98
|
// export type UnknownDevice = {
|
|
89
99
|
// type: 'unacquired';
|
|
90
100
|
// id?: null;
|