@onekeyfe/hd-core 1.2.0-alpha.36 → 1.2.0-alpha.37

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 (69) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +5 -1
  2. package/__tests__/deviceUploadNft.test.ts +187 -0
  3. package/__tests__/homescreen.test.ts +41 -1
  4. package/__tests__/logBlockEvent.test.ts +1 -1
  5. package/__tests__/open-wallet-session.test.ts +161 -44
  6. package/__tests__/pro2Nft.test.ts +108 -0
  7. package/__tests__/protocol-v2-unlock-policy.test.ts +86 -35
  8. package/__tests__/protocol-v2.test.ts +168 -444
  9. package/__tests__/public-pro2-api-boundary.test.ts +1 -0
  10. package/dist/api/BaseMethod.d.ts +3 -1
  11. package/dist/api/BaseMethod.d.ts.map +1 -1
  12. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  13. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  14. package/dist/api/helpers/protocolV2FileWrite.d.ts +2 -0
  15. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  16. package/dist/api/index.d.ts +1 -0
  17. package/dist/api/index.d.ts.map +1 -1
  18. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +30 -0
  19. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
  20. package/dist/core/index.d.ts.map +1 -1
  21. package/dist/events/logBlockEvent.d.ts.map +1 -1
  22. package/dist/index.d.ts +34 -2
  23. package/dist/index.js +500 -462
  24. package/dist/inject.d.ts.map +1 -1
  25. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
  26. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
  27. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
  28. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
  29. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  30. package/dist/types/api/index.d.ts +2 -1
  31. package/dist/types/api/index.d.ts.map +1 -1
  32. package/dist/types/api/protocolV2.d.ts +3 -0
  33. package/dist/types/api/protocolV2.d.ts.map +1 -1
  34. package/dist/utils/homescreen.d.ts +4 -0
  35. package/dist/utils/homescreen.d.ts.map +1 -1
  36. package/dist/utils/index.d.ts +1 -1
  37. package/dist/utils/index.d.ts.map +1 -1
  38. package/dist/utils/patch.d.ts +1 -1
  39. package/dist/utils/patch.d.ts.map +1 -1
  40. package/dist/utils/pro2Nft.d.ts +29 -0
  41. package/dist/utils/pro2Nft.d.ts.map +1 -0
  42. package/dist/utils/pro2Wallpaper.d.ts +10 -0
  43. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  44. package/package.json +4 -4
  45. package/src/api/BaseMethod.ts +7 -9
  46. package/src/api/OpenWalletSession.ts +37 -15
  47. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +29 -30
  48. package/src/api/helpers/protocolV2FileWrite.ts +11 -4
  49. package/src/api/index.ts +1 -0
  50. package/src/api/protocol-v2/DeviceUploadNft.ts +166 -0
  51. package/src/core/index.ts +76 -81
  52. package/src/data/messages/messages-protocol-v2.json +10 -15
  53. package/src/events/logBlockEvent.ts +1 -0
  54. package/src/inject.ts +1 -0
  55. package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
  56. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +117 -0
  57. package/src/protocols/protocol-v2/walletSession.ts +18 -51
  58. package/src/types/api/index.ts +2 -0
  59. package/src/types/api/protocolV2.ts +13 -0
  60. package/src/utils/homescreen.ts +32 -6
  61. package/src/utils/index.ts +6 -1
  62. package/src/utils/pro2Nft.ts +91 -0
  63. package/src/utils/pro2Wallpaper.ts +35 -8
  64. package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
  65. package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
  66. package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
  67. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
  68. package/src/protocols/protocol-v2/lockedError.ts +0 -10
  69. package/src/protocols/protocol-v2/unlockRetry.ts +0 -131
@@ -0,0 +1,108 @@
1
+ import { blake2s } from '@noble/hashes/blake2s';
2
+ import { bytesToHex } from '@noble/hashes/utils';
3
+
4
+ import {
5
+ PRO2_NFT_IMAGE_HEIGHT,
6
+ PRO2_NFT_IMAGE_WIDTH,
7
+ PRO2_NFT_THUMBNAIL_HEIGHT,
8
+ PRO2_NFT_THUMBNAIL_WIDTH,
9
+ buildPro2NftBundle,
10
+ } from '../src/utils/pro2Nft';
11
+
12
+ const rgba = (width: number, height: number, alpha = 0xff) => {
13
+ const data = new Uint8Array(width * height * 4);
14
+ for (let index = 3; index < data.length; index += 4) data[index] = alpha;
15
+ return data;
16
+ };
17
+
18
+ describe('buildPro2NftBundle', () => {
19
+ test('builds the exact Pro2 RGB565 triplet and basename', () => {
20
+ const bundle = buildPro2NftBundle({
21
+ image: {
22
+ width: PRO2_NFT_IMAGE_WIDTH,
23
+ height: PRO2_NFT_IMAGE_HEIGHT,
24
+ rgba: rgba(PRO2_NFT_IMAGE_WIDTH, PRO2_NFT_IMAGE_HEIGHT),
25
+ },
26
+ thumbnail: {
27
+ width: PRO2_NFT_THUMBNAIL_WIDTH,
28
+ height: PRO2_NFT_THUMBNAIL_HEIGHT,
29
+ rgba: rgba(PRO2_NFT_THUMBNAIL_WIDTH, PRO2_NFT_THUMBNAIL_HEIGHT),
30
+ },
31
+ title: 'CryptoPunk #3100',
32
+ subtitle: 'CryptoPunks',
33
+ timestampMs: 1_760_000_000_000,
34
+ });
35
+
36
+ expect(bundle.image.byteLength).toBe(583_212);
37
+ expect(bundle.thumbnail.byteLength).toBe(138_876);
38
+ expect(Array.from(bundle.image.slice(0, 12))).toEqual([
39
+ 0x19, 0x12, 0, 0, 28, 2, 28, 2, 56, 4, 0, 0,
40
+ ]);
41
+ expect(Array.from(bundle.thumbnail.slice(0, 12))).toEqual([
42
+ 0x19, 0x12, 0, 0, 7, 1, 7, 1, 16, 2, 0, 0,
43
+ ]);
44
+ expect(new TextDecoder().decode(bundle.metadata)).toBe(
45
+ '{"title":"CryptoPunk #3100","subtitle":"CryptoPunks"}'
46
+ );
47
+ expect(bytesToHex(blake2s(bundle.image))).toBe(
48
+ 'af4b5f8fe216cfd0ef4a9162a42b5c66a73466f2491066f66f756c4e28cc869e'
49
+ );
50
+ expect(bytesToHex(blake2s(bundle.thumbnail))).toBe(
51
+ '44591afae1163cc14651d77644501c6a110f96506889b825cab0351ff4de857d'
52
+ );
53
+ expect(bytesToHex(blake2s(bundle.metadata))).toBe(
54
+ 'e30d4dc2cddd441c9f1438c783e05a623ccf76f2f6219d4281769dda1b3635b5'
55
+ );
56
+ expect(bundle.basename).toBe('nft-af4b5f8f-1760000000000');
57
+ });
58
+
59
+ test('composites transparent pixels onto black without an alpha plane', () => {
60
+ const image = rgba(PRO2_NFT_IMAGE_WIDTH, PRO2_NFT_IMAGE_HEIGHT);
61
+ image.set([255, 255, 255, 0], 0);
62
+ const bundle = buildPro2NftBundle({
63
+ image: { width: PRO2_NFT_IMAGE_WIDTH, height: PRO2_NFT_IMAGE_HEIGHT, rgba: image },
64
+ thumbnail: {
65
+ width: PRO2_NFT_THUMBNAIL_WIDTH,
66
+ height: PRO2_NFT_THUMBNAIL_HEIGHT,
67
+ rgba: rgba(PRO2_NFT_THUMBNAIL_WIDTH, PRO2_NFT_THUMBNAIL_HEIGHT, 0),
68
+ },
69
+ title: 'NFT',
70
+ subtitle: '',
71
+ timestampMs: 1,
72
+ });
73
+
74
+ expect(bundle.image[1]).toBe(0x12);
75
+ expect(Array.from(bundle.image.slice(12, 14))).toEqual([0, 0]);
76
+ expect(bundle.thumbnail.byteLength).toBe(138_876);
77
+ });
78
+
79
+ test('rejects invalid dimensions and metadata byte lengths', () => {
80
+ const validThumbnail = {
81
+ width: PRO2_NFT_THUMBNAIL_WIDTH,
82
+ height: PRO2_NFT_THUMBNAIL_HEIGHT,
83
+ rgba: rgba(PRO2_NFT_THUMBNAIL_WIDTH, PRO2_NFT_THUMBNAIL_HEIGHT),
84
+ };
85
+ expect(() =>
86
+ buildPro2NftBundle({
87
+ image: { width: 1, height: 1, rgba: new Uint8Array(4) },
88
+ thumbnail: validThumbnail,
89
+ title: 'NFT',
90
+ subtitle: '',
91
+ timestampMs: 1,
92
+ })
93
+ ).toThrow('540x540');
94
+ expect(() =>
95
+ buildPro2NftBundle({
96
+ image: {
97
+ width: PRO2_NFT_IMAGE_WIDTH,
98
+ height: PRO2_NFT_IMAGE_HEIGHT,
99
+ rgba: rgba(PRO2_NFT_IMAGE_WIDTH, PRO2_NFT_IMAGE_HEIGHT),
100
+ },
101
+ thumbnail: validThumbnail,
102
+ title: 'a'.repeat(64),
103
+ subtitle: '',
104
+ timestampMs: 1,
105
+ })
106
+ ).toThrow('1 to 63');
107
+ });
108
+ });
@@ -1,45 +1,96 @@
1
- import * as publicMethods from '../src/api';
2
- import {
3
- PROTOCOL_V2_RETRY_ON_LOCKED_METHODS,
4
- getProtocolV2UnlockPolicy,
5
- } from '../src/protocols/protocol-v2/unlockPolicy';
1
+ import ConfluxSignMessageCIP23 from '../src/api/conflux/ConfluxSignMessageCIP23';
2
+ import DeviceLock from '../src/api/device/DeviceLock';
3
+ import { runMethodWithUnlockPolicy } from '../src/protocols/protocol-v2/unlockPolicyRunner';
6
4
 
7
5
  jest.mock('../src/data/config', () => ({
8
6
  DEFAULT_DOMAIN: 'https://example.com/',
9
7
  getSDKVersion: () => '0.0.0-test',
10
8
  }));
11
9
 
12
- describe('Protocol V2 public method unlock policy', () => {
13
- test('classifies the complete public method table with an explicit replay allowlist', () => {
14
- const publicMethodNames = Object.keys(publicMethods);
15
- const retryMethodNames = new Set<string>(PROTOCOL_V2_RETRY_ON_LOCKED_METHODS);
16
-
17
- expect(publicMethodNames.length).toBeGreaterThan(100);
18
- expect(new Set(PROTOCOL_V2_RETRY_ON_LOCKED_METHODS).size).toBe(
19
- PROTOCOL_V2_RETRY_ON_LOCKED_METHODS.length
20
- );
21
-
22
- for (const methodName of PROTOCOL_V2_RETRY_ON_LOCKED_METHODS) {
23
- expect(publicMethods).toHaveProperty(methodName);
24
- }
25
-
26
- for (const methodName of publicMethodNames) {
27
- expect(getProtocolV2UnlockPolicy(methodName)).toBe(
28
- retryMethodNames.has(methodName) ? 'retry-on-locked' : 'none'
29
- );
30
- }
10
+ describe('Protocol V2 unlock semantics', () => {
11
+ test('wallet business methods inherit the wallet-session unlock requirement', () => {
12
+ const method = new ConfluxSignMessageCIP23({
13
+ id: 1,
14
+ payload: { method: 'confluxSignMessageCIP23' },
15
+ });
16
+
17
+ expect(method.useDevicePassphraseState).toBe(true);
18
+ expect(method.unlockPolicy).toBe('none');
19
+ expect(method.getSupportedProtocols()).toContain('V2');
20
+ });
21
+
22
+ test('lock-free Protocol V2 controls explicitly opt out of wallet-session handling', () => {
23
+ const method = new DeviceLock({
24
+ id: 1,
25
+ payload: { method: 'deviceLock' },
26
+ });
27
+
28
+ method.init();
29
+
30
+ expect(method.useDevicePassphraseState).toBe(false);
31
+ expect(method.unlockPolicy).toBe('none');
31
32
  });
32
33
 
33
- test.each([
34
- 'deviceUnlock',
35
- 'deviceLock',
36
- 'deviceWipe',
37
- 'deviceChangePin',
38
- 'deviceSettings',
39
- 'firmwareUpdateV4',
40
- 'deviceUploadWallpaper',
41
- 'uploadPortfolio',
42
- ])('does not make the state-changing method %s replayable by default', methodName => {
43
- expect(getProtocolV2UnlockPolicy(methodName)).toBe('none');
34
+ test('pre-unlocks an unregistered wallet business method', async () => {
35
+ const features = { unlocked: false };
36
+ const method = {
37
+ name: 'newWalletBusinessMethod',
38
+ unlockPolicy: 'none',
39
+ useDevicePassphraseState: true,
40
+ run: jest.fn().mockResolvedValue({ message: 'ok' }),
41
+ };
42
+ const device = {
43
+ features,
44
+ commands: {
45
+ typedCall: jest.fn().mockResolvedValue({ message: { unlocked: false } }),
46
+ },
47
+ isProtocolV2: () => true,
48
+ isBootloader: () => false,
49
+ isRomloader: () => false,
50
+ updateProtocolV2Status: jest.fn(() => features),
51
+ unlockDevice: jest.fn().mockImplementation(() => {
52
+ features.unlocked = true;
53
+ return Promise.resolve(features);
54
+ }),
55
+ };
56
+
57
+ await expect(runMethodWithUnlockPolicy(method as any, device as any)).resolves.toEqual({
58
+ message: 'ok',
59
+ });
60
+ expect(device.unlockDevice).toHaveBeenCalledTimes(1);
61
+ expect(method.run).toHaveBeenCalledTimes(1);
62
+ });
63
+
64
+ test('runs fresh-status validation before starting unlock', async () => {
65
+ const identityError = new Error('Unexpected device');
66
+ const method = {
67
+ name: 'walletBusinessMethod',
68
+ unlockPolicy: 'none',
69
+ useDevicePassphraseState: true,
70
+ run: jest.fn(),
71
+ };
72
+ const features = { unlocked: false };
73
+ const device = {
74
+ features,
75
+ commands: {
76
+ typedCall: jest.fn().mockResolvedValue({ message: { unlocked: false } }),
77
+ },
78
+ isProtocolV2: () => true,
79
+ isBootloader: () => false,
80
+ isRomloader: () => false,
81
+ updateProtocolV2Status: jest.fn(() => features),
82
+ unlockDevice: jest.fn(),
83
+ };
84
+
85
+ await expect(
86
+ runMethodWithUnlockPolicy(method as any, device as any, {
87
+ afterStatusBeforeUnlock: () => {
88
+ throw identityError;
89
+ },
90
+ })
91
+ ).rejects.toBe(identityError);
92
+
93
+ expect(device.unlockDevice).not.toHaveBeenCalled();
94
+ expect(method.run).not.toHaveBeenCalled();
44
95
  });
45
96
  });