@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.
Files changed (71) hide show
  1. package/README.md +27 -0
  2. package/__tests__/DeviceCommands.test.ts +66 -13
  3. package/__tests__/btc-derive-context-hash.test.ts +200 -0
  4. package/__tests__/core-error-output.test.ts +34 -3
  5. package/__tests__/device-lifecycle-events.test.ts +73 -24
  6. package/__tests__/device-pool-state.test.ts +50 -0
  7. package/__tests__/logBlockEvent.test.ts +43 -0
  8. package/__tests__/protocol-v2-bootloader-mode.test.ts +43 -2
  9. package/__tests__/protocol-v2.test.ts +0 -47
  10. package/__tests__/refresh-device-state.test.ts +19 -17
  11. package/__tests__/sol-sign-offchain-message.test.ts +0 -8
  12. package/__tests__/zcash-sign-pczt.test.ts +212 -0
  13. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  14. package/dist/api/GetDeviceState.d.ts.map +1 -1
  15. package/dist/api/btc/BTCDeriveContextHash.d.ts +16 -0
  16. package/dist/api/btc/BTCDeriveContextHash.d.ts.map +1 -0
  17. package/dist/api/index.d.ts +3 -0
  18. package/dist/api/index.d.ts.map +1 -1
  19. package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
  20. package/dist/api/zcash/ZcashGetAddress.d.ts +12 -0
  21. package/dist/api/zcash/ZcashGetAddress.d.ts.map +1 -0
  22. package/dist/api/zcash/ZcashSignPczt.d.ts +17 -0
  23. package/dist/api/zcash/ZcashSignPczt.d.ts.map +1 -0
  24. package/dist/core/index.d.ts +0 -1
  25. package/dist/core/index.d.ts.map +1 -1
  26. package/dist/device/Device.d.ts +2 -0
  27. package/dist/device/Device.d.ts.map +1 -1
  28. package/dist/device/DeviceCommands.d.ts +4 -4
  29. package/dist/device/DeviceCommands.d.ts.map +1 -1
  30. package/dist/device/DevicePool.d.ts.map +1 -1
  31. package/dist/events/logBlockEvent.d.ts.map +1 -1
  32. package/dist/index.d.ts +51 -5
  33. package/dist/index.js +488 -30
  34. package/dist/inject.d.ts.map +1 -1
  35. package/dist/types/api/btcDeriveContextHash.d.ts +10 -0
  36. package/dist/types/api/btcDeriveContextHash.d.ts.map +1 -0
  37. package/dist/types/api/export.d.ts +3 -0
  38. package/dist/types/api/export.d.ts.map +1 -1
  39. package/dist/types/api/index.d.ts +6 -0
  40. package/dist/types/api/index.d.ts.map +1 -1
  41. package/dist/types/api/zcashGetAddress.d.ts +22 -0
  42. package/dist/types/api/zcashGetAddress.d.ts.map +1 -0
  43. package/dist/types/api/zcashSignPczt.d.ts +9 -0
  44. package/dist/types/api/zcashSignPczt.d.ts.map +1 -0
  45. package/dist/types/params.d.ts +1 -0
  46. package/dist/types/params.d.ts.map +1 -1
  47. package/dist/utils/patch.d.ts +1 -1
  48. package/dist/utils/patch.d.ts.map +1 -1
  49. package/package.json +4 -4
  50. package/src/api/FirmwareUpdateV4.ts +8 -25
  51. package/src/api/GetDeviceState.ts +2 -5
  52. package/src/api/btc/BTCDeriveContextHash.ts +90 -0
  53. package/src/api/index.ts +3 -0
  54. package/src/api/protocol-v2/DeviceReboot.ts +2 -0
  55. package/src/api/zcash/ZcashGetAddress.ts +121 -0
  56. package/src/api/zcash/ZcashSignPczt.ts +153 -0
  57. package/src/core/index.ts +3 -7
  58. package/src/data/messages/messages-protocol-v2.json +138 -1
  59. package/src/data/messages/messages.json +51 -0
  60. package/src/device/Device.ts +11 -1
  61. package/src/device/DeviceCommands.ts +15 -1
  62. package/src/device/DevicePool.ts +12 -2
  63. package/src/events/core.ts +2 -2
  64. package/src/events/logBlockEvent.ts +6 -2
  65. package/src/inject.ts +6 -0
  66. package/src/types/api/btcDeriveContextHash.ts +15 -0
  67. package/src/types/api/export.ts +3 -0
  68. package/src/types/api/index.ts +10 -0
  69. package/src/types/api/zcashGetAddress.ts +36 -0
  70. package/src/types/api/zcashSignPczt.ts +18 -0
  71. 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.1",
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.1",
29
- "@onekeyfe/hd-transport": "1.2.8-alpha.1",
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": "df614dc773e3e1075e5fbcdaee62d10a7a641eed"
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
- isBleStaleBondHardwareError(error) ||
397
- (error instanceof HardwareError &&
398
- (error.errorCode === HardwareErrorCode.ActionCancelled ||
399
- error.errorCode === HardwareErrorCode.FirmwareError ||
400
- error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
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 (isBleStaleBondHardwareError(error) || this.isProtocolV2ReconnectIdentityError(error)) {
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 (isBleStaleBondHardwareError(error) || this.isProtocolV2ReconnectIdentityError(error)) {
3025
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
3043
3026
  throw error;
3044
3027
  }
3045
3028
  shouldReconnect = true;
@@ -1,4 +1,4 @@
1
- import { ERRORS, HardwareErrorCode, createDeviceNotSupportMethodError } from '@onekeyfe/hd-shared';
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 createDeviceNotSupportMethodError(
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.toMessageObject();
1592
+ const deviceObject = device.toDisconnectMessageObject();
1597
1593
  if (!deviceObject) return;
1598
1594
  postMessage(createDeviceMessage(DEVICE.DISCONNECT, { device: deviceObject }));
1599
1595
  };