@onekeyfe/hd-core 1.2.8-alpha.1 → 1.2.8-alpha.100
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__/DeviceCommands.test.ts +66 -13
- package/__tests__/btc-derive-context-hash.test.ts +200 -0
- package/__tests__/core-error-output.test.ts +34 -3
- package/__tests__/device-lifecycle-events.test.ts +73 -24
- package/__tests__/device-pool-state.test.ts +50 -0
- package/__tests__/logBlockEvent.test.ts +43 -0
- package/__tests__/protocol-v2-bootloader-mode.test.ts +43 -2
- package/__tests__/protocol-v2.test.ts +0 -47
- package/__tests__/refresh-device-state.test.ts +19 -17
- package/__tests__/sol-sign-offchain-message.test.ts +0 -8
- package/__tests__/zcash-sign-pczt.test.ts +212 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetDeviceState.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 +3 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
- package/dist/api/zcash/ZcashGetAddress.d.ts +12 -0
- package/dist/api/zcash/ZcashGetAddress.d.ts.map +1 -0
- package/dist/api/zcash/ZcashSignPczt.d.ts +17 -0
- package/dist/api/zcash/ZcashSignPczt.d.ts.map +1 -0
- package/dist/core/index.d.ts +0 -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/DeviceCommands.d.ts +4 -4
- package/dist/device/DeviceCommands.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 +51 -5
- package/dist/index.js +488 -30
- 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 +3 -0
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +6 -0
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/zcashGetAddress.d.ts +22 -0
- package/dist/types/api/zcashGetAddress.d.ts.map +1 -0
- package/dist/types/api/zcashSignPczt.d.ts +9 -0
- package/dist/types/api/zcashSignPczt.d.ts.map +1 -0
- package/dist/types/params.d.ts +1 -0
- package/dist/types/params.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/FirmwareUpdateV4.ts +8 -25
- package/src/api/GetDeviceState.ts +2 -5
- package/src/api/btc/BTCDeriveContextHash.ts +90 -0
- package/src/api/index.ts +3 -0
- package/src/api/protocol-v2/DeviceReboot.ts +2 -0
- package/src/api/zcash/ZcashGetAddress.ts +121 -0
- package/src/api/zcash/ZcashSignPczt.ts +153 -0
- package/src/core/index.ts +3 -7
- package/src/data/messages/messages-protocol-v2.json +138 -1
- package/src/data/messages/messages.json +51 -0
- package/src/device/Device.ts +11 -1
- package/src/device/DeviceCommands.ts +15 -1
- package/src/device/DevicePool.ts +12 -2
- package/src/events/core.ts +2 -2
- package/src/events/logBlockEvent.ts +6 -2
- package/src/inject.ts +6 -0
- package/src/types/api/btcDeriveContextHash.ts +15 -0
- package/src/types/api/export.ts +3 -0
- package/src/types/api/index.ts +10 -0
- package/src/types/api/zcashGetAddress.ts +36 -0
- package/src/types/api/zcashSignPczt.ts +18 -0
- package/src/types/params.ts +1 -1
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.100",
|
|
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.100",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.2.8-alpha.100",
|
|
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": "53ada5b79a51f0edf8a7392f70c8da063144d33c"
|
|
50
50
|
}
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
EDeviceType,
|
|
3
|
-
ERRORS,
|
|
4
|
-
HardwareError,
|
|
5
|
-
HardwareErrorCode,
|
|
6
|
-
isBleStaleBondHardwareError,
|
|
7
|
-
wait,
|
|
8
|
-
} from '@onekeyfe/hd-shared';
|
|
1
|
+
import { EDeviceType, ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
|
|
9
2
|
import JSZip from 'jszip';
|
|
10
3
|
import {
|
|
11
4
|
DeviceRebootType,
|
|
@@ -393,12 +386,11 @@ const getProtocolV2ErrorCode = (error: unknown): number | string | undefined =>
|
|
|
393
386
|
};
|
|
394
387
|
|
|
395
388
|
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
396
|
-
|
|
397
|
-
(error
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE));
|
|
389
|
+
error instanceof HardwareError &&
|
|
390
|
+
(error.errorCode === HardwareErrorCode.ActionCancelled ||
|
|
391
|
+
error.errorCode === HardwareErrorCode.FirmwareError ||
|
|
392
|
+
error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
|
|
393
|
+
error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE);
|
|
402
394
|
|
|
403
395
|
const isProtocolV2TargetStatusFinished = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) =>
|
|
404
396
|
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_FINISHED;
|
|
@@ -1880,9 +1872,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1880
1872
|
if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
|
|
1881
1873
|
return true;
|
|
1882
1874
|
} catch (error) {
|
|
1883
|
-
if (isBleStaleBondHardwareError(error)) {
|
|
1884
|
-
throw error;
|
|
1885
|
-
}
|
|
1886
1875
|
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
1887
1876
|
return false;
|
|
1888
1877
|
}
|
|
@@ -1976,7 +1965,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1976
1965
|
}
|
|
1977
1966
|
lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
|
|
1978
1967
|
} catch (error) {
|
|
1979
|
-
if (
|
|
1968
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
1980
1969
|
throw error;
|
|
1981
1970
|
}
|
|
1982
1971
|
shouldReconnect = true;
|
|
@@ -2274,9 +2263,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2274
2263
|
return processedSize + source.size;
|
|
2275
2264
|
} catch (error) {
|
|
2276
2265
|
this.throwIfAborted();
|
|
2277
|
-
if (isBleStaleBondHardwareError(error)) {
|
|
2278
|
-
throw error;
|
|
2279
|
-
}
|
|
2280
2266
|
lastError = error;
|
|
2281
2267
|
if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
2282
2268
|
await this.recoverProtocolV2FileTransfer();
|
|
@@ -2944,9 +2930,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2944
2930
|
return;
|
|
2945
2931
|
}
|
|
2946
2932
|
} catch (error) {
|
|
2947
|
-
if (isBleStaleBondHardwareError(error)) {
|
|
2948
|
-
throw error;
|
|
2949
|
-
}
|
|
2950
2933
|
Log.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
|
|
2951
2934
|
}
|
|
2952
2935
|
await this.protocolV2Reboot(DeviceRebootType.Normal);
|
|
@@ -3039,7 +3022,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
3039
3022
|
'Protocol V2 device is still in bootloader mode'
|
|
3040
3023
|
);
|
|
3041
3024
|
} catch (error) {
|
|
3042
|
-
if (
|
|
3025
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
3043
3026
|
throw error;
|
|
3044
3027
|
}
|
|
3045
3028
|
shouldReconnect = true;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ERRORS, HardwareErrorCode
|
|
1
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import { UI_REQUEST } from '../constants/ui-request';
|
|
4
4
|
import { BaseMethod } from './BaseMethod';
|
|
@@ -49,10 +49,7 @@ export default class GetDeviceState extends BaseMethod<
|
|
|
49
49
|
this.device.isProtocolV2() &&
|
|
50
50
|
isLoaderMode(state.status.mode)
|
|
51
51
|
) {
|
|
52
|
-
throw
|
|
53
|
-
`${this.name}:${this.params.scope}`,
|
|
54
|
-
this.device.getCurrentFirmwareType()
|
|
55
|
-
);
|
|
52
|
+
throw ERRORS.TypedError(HardwareErrorCode.NotAllowInBootloaderMode);
|
|
56
53
|
}
|
|
57
54
|
return state;
|
|
58
55
|
}
|
|
@@ -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';
|
|
@@ -174,3 +175,5 @@ export { default as benfenSignTransaction } from './benfen/BenfenSignTransaction
|
|
|
174
175
|
|
|
175
176
|
export { default as neoGetAddress } from './neo/NeoGetAddress';
|
|
176
177
|
export { default as neoSignTransaction } from './neo/NeoSignTransaction';
|
|
178
|
+
export { default as zcashGetAddress } from './zcash/ZcashGetAddress';
|
|
179
|
+
export { default as zcashSignPczt } from './zcash/ZcashSignPczt';
|
|
@@ -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';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { ZcashAddressScope, ZcashAddressType } from '@onekeyfe/hd-transport';
|
|
2
|
+
|
|
3
|
+
import { BaseMethod } from '../BaseMethod';
|
|
4
|
+
import { serializedPath, toHardened, validatePath } from '../helpers/pathUtils';
|
|
5
|
+
import { invalidParameter, validateParams, validateResult } from '../helpers/paramsValidator';
|
|
6
|
+
import { UI_REQUEST } from '../../constants/ui-request';
|
|
7
|
+
|
|
8
|
+
import type { ZcashGetAddress as HardwareZcashGetAddress } from '@onekeyfe/hd-transport';
|
|
9
|
+
import type { ZcashGetAddressParams, ZcashAddress } from '../../types/api/zcashGetAddress';
|
|
10
|
+
import type { DeviceFirmwareRange } from '../../types';
|
|
11
|
+
|
|
12
|
+
export default class ZcashGetAddress extends BaseMethod<HardwareZcashGetAddress[]> {
|
|
13
|
+
hasBundle = false;
|
|
14
|
+
|
|
15
|
+
getSupportedProtocols() {
|
|
16
|
+
return ['V2'] as const;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
init() {
|
|
20
|
+
this.checkDeviceId = true;
|
|
21
|
+
this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.NOT_INITIALIZE];
|
|
22
|
+
this.strictCheckDeviceSupport = true;
|
|
23
|
+
|
|
24
|
+
this.hasBundle = !!this.payload?.bundle;
|
|
25
|
+
const payload = this.hasBundle ? this.payload : { bundle: [this.payload] };
|
|
26
|
+
|
|
27
|
+
validateParams(payload, [{ name: 'bundle', type: 'array' }]);
|
|
28
|
+
|
|
29
|
+
this.params = [];
|
|
30
|
+
payload.bundle.forEach((batch: ZcashGetAddressParams) => {
|
|
31
|
+
// ZIP-32 account path: m/32'/133'/account'
|
|
32
|
+
const addressN = validatePath(batch.path, 3);
|
|
33
|
+
if (
|
|
34
|
+
addressN.length !== 3 ||
|
|
35
|
+
addressN[0] !== toHardened(32) ||
|
|
36
|
+
addressN[1] !== toHardened(133) ||
|
|
37
|
+
addressN[2] < toHardened(0) ||
|
|
38
|
+
addressN[2] > 0xffffffff
|
|
39
|
+
) {
|
|
40
|
+
throw invalidParameter("Zcash requires a ZIP-32 account path m/32'/133'/account'");
|
|
41
|
+
}
|
|
42
|
+
const addressType = batch.addressType ?? ZcashAddressType.UNIFIED_ORCHARD_P2PKH;
|
|
43
|
+
const scope = batch.scope ?? ZcashAddressScope.EXTERNAL;
|
|
44
|
+
const diversifierIndex = batch.diversifierIndex ?? 0;
|
|
45
|
+
if (
|
|
46
|
+
![
|
|
47
|
+
ZcashAddressType.UNIFIED_ORCHARD_P2PKH,
|
|
48
|
+
ZcashAddressType.TRANSPARENT_P2PKH,
|
|
49
|
+
ZcashAddressType.UNIFIED_ORCHARD,
|
|
50
|
+
].includes(addressType) ||
|
|
51
|
+
![ZcashAddressScope.EXTERNAL, ZcashAddressScope.INTERNAL].includes(scope)
|
|
52
|
+
) {
|
|
53
|
+
throw invalidParameter('Invalid Zcash address type or scope');
|
|
54
|
+
}
|
|
55
|
+
const maxIndex = addressType === ZcashAddressType.UNIFIED_ORCHARD ? 0xffffffff : 0x7fffffff;
|
|
56
|
+
if (
|
|
57
|
+
!Number.isSafeInteger(diversifierIndex) ||
|
|
58
|
+
diversifierIndex < 0 ||
|
|
59
|
+
diversifierIndex > maxIndex
|
|
60
|
+
) {
|
|
61
|
+
throw invalidParameter('Invalid Zcash diversifier index');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
validateParams(batch, [
|
|
65
|
+
{ name: 'path', required: true },
|
|
66
|
+
{ name: 'showOnOneKey', type: 'boolean' },
|
|
67
|
+
{ name: 'includeUfvk', type: 'boolean' },
|
|
68
|
+
{ name: 'includeSeedFingerprint', type: 'boolean' },
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
this.params.push({
|
|
72
|
+
address_n: addressN,
|
|
73
|
+
address_type: addressType,
|
|
74
|
+
scope,
|
|
75
|
+
diversifier_index: diversifierIndex,
|
|
76
|
+
show_display: batch.showOnOneKey ?? true,
|
|
77
|
+
include_ufvk: batch.includeUfvk ?? false,
|
|
78
|
+
include_seed_fingerprint: batch.includeSeedFingerprint ?? false,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
getVersionRange(): DeviceFirmwareRange {
|
|
84
|
+
return {
|
|
85
|
+
model_pro2: {
|
|
86
|
+
min: '0.0.0',
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async run() {
|
|
92
|
+
const responses: ZcashAddress[] = [];
|
|
93
|
+
|
|
94
|
+
for (let i = 0; i < this.params.length; i++) {
|
|
95
|
+
const param = this.params[i];
|
|
96
|
+
|
|
97
|
+
const res = await this.device.commands.typedCall('ZcashGetAddress', 'ZcashAddress', {
|
|
98
|
+
...param,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const path = serializedPath(param.address_n);
|
|
102
|
+
responses.push({
|
|
103
|
+
path,
|
|
104
|
+
address: res.message.address,
|
|
105
|
+
ufvk: res.message.ufvk,
|
|
106
|
+
seedFingerprint: res.message.seed_fingerprint,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
this.postPreviousAddressMessage({
|
|
110
|
+
address: res.message.address,
|
|
111
|
+
path,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
validateResult(responses, ['address'], {
|
|
116
|
+
expectedLength: this.params.length,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return Promise.resolve(this.hasBundle ? responses : responses[0]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
2
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
|
+
|
|
4
|
+
import { BaseMethod } from '../BaseMethod';
|
|
5
|
+
import { invalidParameter, validateParams } from '../helpers/paramsValidator';
|
|
6
|
+
import { formatAnyHex } from '../helpers/hexUtils';
|
|
7
|
+
import { UI_REQUEST } from '../../constants/ui-request';
|
|
8
|
+
|
|
9
|
+
import type { ZcashSignPczt as HardwareZcashSignPczt } from '@onekeyfe/hd-transport';
|
|
10
|
+
import type { TypedCall, TypedResponseMessage } from '../../device/DeviceCommands';
|
|
11
|
+
import type { ZcashSignedPczt } from '../../types/api/zcashSignPczt';
|
|
12
|
+
import type { DeviceFirmwareRange } from '../../types';
|
|
13
|
+
|
|
14
|
+
// Firmware buffers the whole PCZT in RAM in both directions.
|
|
15
|
+
export const ZCASH_PCZT_MAX_BYTES = 24 * 1024;
|
|
16
|
+
const CHUNK_BYTE_SIZE = 1024;
|
|
17
|
+
|
|
18
|
+
export default class ZcashSignPczt extends BaseMethod<HardwareZcashSignPczt> {
|
|
19
|
+
pczt: Buffer = Buffer.alloc(0);
|
|
20
|
+
|
|
21
|
+
getSupportedProtocols() {
|
|
22
|
+
return ['V2'] as const;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
init() {
|
|
26
|
+
this.checkDeviceId = true;
|
|
27
|
+
this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.NOT_INITIALIZE];
|
|
28
|
+
this.strictCheckDeviceSupport = true;
|
|
29
|
+
|
|
30
|
+
validateParams(this.payload, [{ name: 'pczt', type: 'hexString', required: true }]);
|
|
31
|
+
|
|
32
|
+
this.pczt = Buffer.from(formatAnyHex(this.payload.pczt), 'hex');
|
|
33
|
+
if (this.pczt.length === 0) {
|
|
34
|
+
throw invalidParameter('pczt is empty');
|
|
35
|
+
}
|
|
36
|
+
if (this.pczt.length > ZCASH_PCZT_MAX_BYTES) {
|
|
37
|
+
throw invalidParameter(`pczt exceeds ${ZCASH_PCZT_MAX_BYTES} bytes (${this.pczt.length})`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.params = {
|
|
41
|
+
pczt_length: this.pczt.length,
|
|
42
|
+
pczt_initial_chunk: bytesToHex(this.pczt.subarray(0, CHUNK_BYTE_SIZE)),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getVersionRange(): DeviceFirmwareRange {
|
|
47
|
+
return {
|
|
48
|
+
model_pro2: {
|
|
49
|
+
min: '0.0.0',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Upload: the device decides every chunk length via ZcashPcztChunkRequest and
|
|
55
|
+
// the host must answer with exactly that many bytes.
|
|
56
|
+
uploadPczt = async (
|
|
57
|
+
typedCall: TypedCall,
|
|
58
|
+
res: TypedResponseMessage<'ZcashSignedPczt'> | TypedResponseMessage<'ZcashPcztChunkRequest'>,
|
|
59
|
+
offset: number
|
|
60
|
+
): Promise<TypedResponseMessage<'ZcashSignedPczt'>> => {
|
|
61
|
+
if (res.type === 'ZcashSignedPczt') {
|
|
62
|
+
if (offset !== this.pczt.length) {
|
|
63
|
+
throw ERRORS.TypedError(
|
|
64
|
+
HardwareErrorCode.ResponseUnexpectTypeError,
|
|
65
|
+
'device completed signing before receiving the whole pczt'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return res;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const { chunk_length: chunkLength } = res.message;
|
|
72
|
+
if (
|
|
73
|
+
!Number.isSafeInteger(chunkLength) ||
|
|
74
|
+
chunkLength <= 0 ||
|
|
75
|
+
offset + chunkLength > this.pczt.length
|
|
76
|
+
) {
|
|
77
|
+
throw ERRORS.TypedError(
|
|
78
|
+
HardwareErrorCode.ResponseUnexpectTypeError,
|
|
79
|
+
`device requested ${chunkLength} bytes at offset ${offset} of ${this.pczt.length}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const chunk = this.pczt.subarray(offset, offset + chunkLength);
|
|
84
|
+
const next = await typedCall(
|
|
85
|
+
'ZcashPcztChunkAck',
|
|
86
|
+
['ZcashSignedPczt', 'ZcashPcztChunkRequest'],
|
|
87
|
+
{
|
|
88
|
+
data_chunk: bytesToHex(chunk),
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return this.uploadPczt(typedCall, next, offset + chunkLength);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Download: the host decides chunk lengths via ZcashSignedPcztChunkRequest
|
|
96
|
+
// until the announced pczt_length has been received.
|
|
97
|
+
downloadSignedPczt = async (
|
|
98
|
+
typedCall: TypedCall,
|
|
99
|
+
res: TypedResponseMessage<'ZcashSignedPczt'>
|
|
100
|
+
): Promise<Buffer> => {
|
|
101
|
+
const { pczt_length: total, pczt_initial_chunk: initial } = res.message;
|
|
102
|
+
if (!Number.isSafeInteger(total) || total <= 0 || total > ZCASH_PCZT_MAX_BYTES) {
|
|
103
|
+
throw ERRORS.TypedError(
|
|
104
|
+
HardwareErrorCode.ResponseUnexpectTypeError,
|
|
105
|
+
`device announced invalid signed pczt length ${total}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const parts: Buffer[] = [Buffer.from(initial ?? '', 'hex')];
|
|
110
|
+
let received = parts[0].length;
|
|
111
|
+
if (received > total) {
|
|
112
|
+
throw ERRORS.TypedError(
|
|
113
|
+
HardwareErrorCode.ResponseUnexpectTypeError,
|
|
114
|
+
'device returned more initial pczt bytes than announced'
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
while (received < total) {
|
|
119
|
+
const chunkLength = Math.min(CHUNK_BYTE_SIZE, total - received);
|
|
120
|
+
const ack = await typedCall('ZcashSignedPcztChunkRequest', 'ZcashPcztChunkAck', {
|
|
121
|
+
chunk_length: chunkLength,
|
|
122
|
+
});
|
|
123
|
+
const chunk = Buffer.from(ack.message.data_chunk ?? '', 'hex');
|
|
124
|
+
if (chunk.length === 0 || received + chunk.length > total) {
|
|
125
|
+
throw ERRORS.TypedError(
|
|
126
|
+
HardwareErrorCode.ResponseUnexpectTypeError,
|
|
127
|
+
`device returned ${chunk.length} bytes at offset ${received} of ${total}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
parts.push(chunk);
|
|
131
|
+
received += chunk.length;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return Buffer.concat(parts);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
async run(): Promise<ZcashSignedPczt> {
|
|
138
|
+
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
139
|
+
|
|
140
|
+
const first = await typedCall('ZcashSignPczt', ['ZcashSignedPczt', 'ZcashPcztChunkRequest'], {
|
|
141
|
+
...this.params,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const signed = await this.uploadPczt(
|
|
145
|
+
typedCall,
|
|
146
|
+
first,
|
|
147
|
+
Math.min(CHUNK_BYTE_SIZE, this.pczt.length)
|
|
148
|
+
);
|
|
149
|
+
const pczt = await this.downloadSignedPczt(typedCall, signed);
|
|
150
|
+
|
|
151
|
+
return { pczt: bytesToHex(pczt) };
|
|
152
|
+
}
|
|
153
|
+
}
|
package/src/core/index.ts
CHANGED
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
createNeedUpgradeFirmwareHardwareError,
|
|
24
24
|
createNewFirmwareForceUpdateHardwareError,
|
|
25
25
|
createNewFirmwareUnReleaseHardwareError,
|
|
26
|
-
isBleStaleBondHardwareError,
|
|
27
26
|
} from '@onekeyfe/hd-shared';
|
|
28
27
|
|
|
29
28
|
import { LoggerNames, enableLog, getLogger, setLoggerPostMessage, wait } from '../utils';
|
|
@@ -1035,10 +1034,6 @@ export function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unkn
|
|
|
1035
1034
|
);
|
|
1036
1035
|
}
|
|
1037
1036
|
|
|
1038
|
-
export function isTerminalBleStaleBondError(error: unknown) {
|
|
1039
|
-
return isBleStaleBondHardwareError(error);
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
1037
|
export function isDeviceIdentityMismatchError(error: unknown) {
|
|
1043
1038
|
return (
|
|
1044
1039
|
(error as { errorCode?: unknown })?.errorCode === HardwareErrorCode.DeviceCheckDeviceIdError
|
|
@@ -1277,6 +1272,7 @@ const ensureConnected = async (
|
|
|
1277
1272
|
HardwareErrorCode.BridgeNotInstalled,
|
|
1278
1273
|
HardwareErrorCode.BridgeTimeoutError,
|
|
1279
1274
|
HardwareErrorCode.BridgeNeedsPermission,
|
|
1275
|
+
HardwareErrorCode.WebUsbDeviceAccessError,
|
|
1280
1276
|
].includes(error.errorCode)
|
|
1281
1277
|
) {
|
|
1282
1278
|
_deviceList = undefined;
|
|
@@ -1363,6 +1359,7 @@ const ensureConnected = async (
|
|
|
1363
1359
|
HardwareErrorCode.DeviceDetectInBootloaderMode,
|
|
1364
1360
|
HardwareErrorCode.BleCharacteristicNotifyChangeFailure,
|
|
1365
1361
|
HardwareErrorCode.BridgeNeedsPermission,
|
|
1362
|
+
HardwareErrorCode.WebUsbDeviceAccessError,
|
|
1366
1363
|
HardwareErrorCode.DeviceInterruptedFromUser,
|
|
1367
1364
|
HardwareErrorCode.CallQueueActionCancelled,
|
|
1368
1365
|
].includes(error.errorCode) ||
|
|
@@ -1370,7 +1367,6 @@ const ensureConnected = async (
|
|
|
1370
1367
|
[HardwareErrorCode.BleDeviceDisconnected, HardwareErrorCode.PollingTimeout].includes(
|
|
1371
1368
|
error.errorCode
|
|
1372
1369
|
)) ||
|
|
1373
|
-
isTerminalBleStaleBondError(error) ||
|
|
1374
1370
|
isDeviceIdentityMismatchError(error)
|
|
1375
1371
|
) {
|
|
1376
1372
|
reject(error);
|
|
@@ -1593,7 +1589,7 @@ const onDeviceConnectHandler = (device: Device) => {
|
|
|
1593
1589
|
|
|
1594
1590
|
const onDeviceDisconnectHandler = (device: Device) => {
|
|
1595
1591
|
device.clearPreInitialized();
|
|
1596
|
-
const deviceObject = device.
|
|
1592
|
+
const deviceObject = device.toDisconnectMessageObject();
|
|
1597
1593
|
if (!deviceObject) return;
|
|
1598
1594
|
postMessage(createDeviceMessage(DEVICE.DISCONNECT, { device: deviceObject }));
|
|
1599
1595
|
};
|