@onekeyfe/hd-core 1.2.0-alpha.33 → 1.2.0-alpha.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/DeviceCommands.test.ts +84 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +31 -0
- package/__tests__/core-dispose.test.ts +3 -3
- package/__tests__/device-lifecycle-events.test.ts +52 -0
- package/__tests__/device-pool-state.test.ts +14 -2
- package/__tests__/device-settings.test.ts +25 -0
- package/__tests__/device-state-mapper.test.ts +13 -2
- package/__tests__/device-state-projector.test.ts +2 -2
- package/__tests__/device-utils.test.ts +1 -0
- package/__tests__/device-wallet-session-store.test.ts +137 -17
- package/__tests__/evmSignTransaction.test.ts +69 -0
- package/__tests__/get-device-state.test.ts +78 -30
- package/__tests__/kaspaSignTransaction.test.ts +9 -0
- package/__tests__/known-device-public-types.type-test.ts +3 -0
- package/__tests__/logBlockEvent.test.ts +89 -5
- package/__tests__/open-wallet-session.test.ts +499 -60
- package/__tests__/pro2Wallpaper.test.ts +1 -1
- package/__tests__/protocol-v2.test.ts +1180 -169
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +2 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.d.ts.map +1 -1
- package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/evm/protocol.d.ts +3 -0
- package/dist/api/evm/protocol.d.ts.map +1 -0
- package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +10 -4
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts +1 -0
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts.map +1 -1
- package/dist/device/DeviceStateProjector.d.ts.map +1 -1
- package/dist/device/DeviceWalletSessionStore.d.ts +7 -0
- package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts +7 -0
- package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -0
- package/dist/events/logBlockEvent.d.ts +2 -0
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +25 -11
- package/dist/index.js +1037 -838
- package/dist/protocols/protocol-v2/features.d.ts +17 -6
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +2 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +1 -0
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +1 -10
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +2 -2
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +6 -3
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/params.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +0 -5
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +11 -81
- package/src/api/FirmwareUpdateV4.ts +59 -25
- package/src/api/GetPassphraseState.ts +11 -2
- package/src/api/OpenWalletSession.ts +16 -8
- package/src/api/device/DeviceSettings.ts +8 -1
- package/src/api/evm/EVMGetAddress.ts +2 -2
- package/src/api/evm/EVMGetPublicKey.ts +2 -2
- package/src/api/evm/EVMSignMessage.ts +2 -2
- package/src/api/evm/EVMSignTransaction.ts +2 -2
- package/src/api/evm/EVMSignTypedData.ts +35 -45
- package/src/api/evm/EVMVerifyMessage.ts +2 -2
- package/src/api/evm/protocol.ts +6 -0
- package/src/api/kaspa/KaspaSignTransaction.ts +7 -0
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +2 -2
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
- package/src/core/index.ts +7 -5
- package/src/data/messages/messages-protocol-v2.json +393 -485
- package/src/device/Device.ts +98 -44
- package/src/device/DeviceCommands.ts +12 -2
- package/src/device/DeviceConnector.ts +10 -7
- package/src/device/DevicePool.ts +5 -3
- package/src/device/DeviceStateMapper.ts +5 -3
- package/src/device/DeviceStateProjector.ts +0 -7
- package/src/device/DeviceWalletSessionStore.ts +43 -4
- package/src/deviceProfile/buildDeviceFeatures.ts +11 -4
- package/src/deviceProfile/protocolV2DeviceIdentity.ts +32 -0
- package/src/events/logBlockEvent.ts +84 -1
- package/src/protocols/protocol-v2/deviceSession.ts +1 -1
- package/src/protocols/protocol-v2/features.ts +58 -16
- package/src/protocols/protocol-v2/index.ts +5 -0
- package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +8 -3
- package/src/protocols/protocol-v2/unlockRetry.ts +18 -7
- package/src/protocols/protocol-v2/walletSession.ts +144 -62
- package/src/types/api/protocolV2.ts +2 -2
- package/src/types/device.ts +8 -2
- package/src/types/params.ts +2 -1
- package/src/utils/deviceFeaturesCompat.ts +8 -1
- package/src/utils/pro2Wallpaper.ts +3 -9
- package/tsconfig.type-tests.json +5 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import EVMSignTransaction from '../src/api/evm/EVMSignTransaction';
|
|
2
|
+
import TransportManager from '../src/data-manager/TransportManager';
|
|
2
3
|
|
|
3
4
|
import type { EVMTransactionEIP7702 } from '../src/types';
|
|
4
5
|
|
|
@@ -17,13 +18,17 @@ jest.mock('../src/device/Device', () => ({
|
|
|
17
18
|
Device: jest.fn(),
|
|
18
19
|
}));
|
|
19
20
|
|
|
21
|
+
const mockTransportManager = jest.mocked(TransportManager);
|
|
22
|
+
|
|
20
23
|
describe('EVMSignTransaction EIP-7702', () => {
|
|
21
24
|
let mockDevice: any;
|
|
22
25
|
let mockTypedCall: jest.Mock;
|
|
23
26
|
|
|
24
27
|
beforeEach(() => {
|
|
28
|
+
mockTransportManager.getProtocolV1MessageSchema.mockReturnValue('v1CurrentSchema');
|
|
25
29
|
mockTypedCall = jest.fn();
|
|
26
30
|
mockDevice = {
|
|
31
|
+
isProtocolV2: jest.fn(() => false),
|
|
27
32
|
commands: {
|
|
28
33
|
typedCall: {
|
|
29
34
|
bind: jest.fn(() => mockTypedCall),
|
|
@@ -32,6 +37,70 @@ describe('EVMSignTransaction EIP-7702', () => {
|
|
|
32
37
|
};
|
|
33
38
|
});
|
|
34
39
|
|
|
40
|
+
describe('Protocol-specific schema routing', () => {
|
|
41
|
+
const transaction: EVMTransactionEIP7702 = {
|
|
42
|
+
to: '0x4Cd241E8d1510e30b2076397afc7508Ae59C66c9',
|
|
43
|
+
value: '0x0',
|
|
44
|
+
gasLimit: '0x5208',
|
|
45
|
+
nonce: '0x0',
|
|
46
|
+
chainId: 1,
|
|
47
|
+
maxFeePerGas: '0xbebc200',
|
|
48
|
+
maxPriorityFeePerGas: '0x9502f900',
|
|
49
|
+
authorizationList: [
|
|
50
|
+
{
|
|
51
|
+
chainId: 1,
|
|
52
|
+
address: '0x4Cd241E8d1510e30b2076397afc7508Ae59C66c9',
|
|
53
|
+
nonce: '0x1',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const createTransactionMethod = () => {
|
|
59
|
+
const method = new EVMSignTransaction({
|
|
60
|
+
id: 1,
|
|
61
|
+
payload: {
|
|
62
|
+
method: 'evmSignTransaction',
|
|
63
|
+
path: "m/44'/60'/0'/0/0",
|
|
64
|
+
transaction,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
method.device = mockDevice;
|
|
68
|
+
method.init();
|
|
69
|
+
return method;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
it('keeps Protocol V2 on current OneKey messages after a legacy V1 device', async () => {
|
|
73
|
+
mockTransportManager.getProtocolV1MessageSchema.mockReturnValue('v1LegacySchema');
|
|
74
|
+
mockDevice.isProtocolV2.mockReturnValue(true);
|
|
75
|
+
mockTypedCall.mockResolvedValue({
|
|
76
|
+
message: {
|
|
77
|
+
signature_v: 27,
|
|
78
|
+
signature_r: '01',
|
|
79
|
+
signature_s: '02',
|
|
80
|
+
authorization_signatures: [],
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await expect(createTransactionMethod().run()).resolves.toMatchObject({
|
|
85
|
+
v: '0x1b',
|
|
86
|
+
});
|
|
87
|
+
expect(mockTypedCall).toHaveBeenCalledWith(
|
|
88
|
+
'EthereumSignTxEIP7702OneKey',
|
|
89
|
+
'EthereumTxRequestOneKey',
|
|
90
|
+
expect.any(Object)
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('continues to use legacy messages for a Protocol V1 legacy device', async () => {
|
|
95
|
+
mockTransportManager.getProtocolV1MessageSchema.mockReturnValue('v1LegacySchema');
|
|
96
|
+
|
|
97
|
+
await expect(createTransactionMethod().run()).rejects.toThrow(
|
|
98
|
+
'EIP7702 not supported by Trezor'
|
|
99
|
+
);
|
|
100
|
+
expect(mockTypedCall).not.toHaveBeenCalled();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
35
104
|
describe('EIP-7702 Transaction Detection', () => {
|
|
36
105
|
it('should detect EIP-7702 transaction with authorization list', () => {
|
|
37
106
|
const transaction: EVMTransactionEIP7702 = {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { DeviceType } from '@onekeyfe/hd-transport';
|
|
2
3
|
|
|
3
4
|
import { Device } from '../src/device/Device';
|
|
4
5
|
import { DEVICE } from '../src/events';
|
|
6
|
+
import { PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE } from '../src/protocols/protocol-v2';
|
|
5
7
|
|
|
6
8
|
jest.mock('../src/data/config', () => ({
|
|
7
9
|
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
@@ -28,6 +30,18 @@ const createV1Device = (typedCall: jest.Mock) => {
|
|
|
28
30
|
return device;
|
|
29
31
|
};
|
|
30
32
|
|
|
33
|
+
const protocolV2ApplicationInfo = {
|
|
34
|
+
version: 1,
|
|
35
|
+
build_fingerprint: 'application__5.0.0__abcdef0__PROD__RELEASE',
|
|
36
|
+
supported_messages: [PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const getProtocolV2LoaderInfo = (mode: 'bootloader' | 'romloader') => ({
|
|
40
|
+
version: 1,
|
|
41
|
+
build_fingerprint: `${mode}__1.0.0__abcdef0__PROD__RELEASE`,
|
|
42
|
+
supported_messages: [],
|
|
43
|
+
});
|
|
44
|
+
|
|
31
45
|
describe('getDeviceState', () => {
|
|
32
46
|
test('does not expose the internal wallet session', async () => {
|
|
33
47
|
const device = createV2Device(jest.fn());
|
|
@@ -39,45 +53,55 @@ describe('getDeviceState', () => {
|
|
|
39
53
|
});
|
|
40
54
|
|
|
41
55
|
test('hydrates Protocol V2 with separate DeviceInfoGet and DeviceStatusGet', async () => {
|
|
42
|
-
const typedCall = jest.fn().mockImplementation((requestType: string) =>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
57
|
+
if (requestType === 'DeviceInfoGet') {
|
|
58
|
+
return {
|
|
59
|
+
message: {
|
|
60
|
+
protocol_version: 2,
|
|
61
|
+
hw: { serial_no: 'SERIAL-1' },
|
|
62
|
+
fw: { application: { version: '5.0.0' } },
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
67
|
+
return { message: protocolV2ApplicationInfo };
|
|
68
|
+
}
|
|
69
|
+
return { message: { init_states: true, unlocked: true } };
|
|
70
|
+
});
|
|
52
71
|
const device = createV2Device(typedCall);
|
|
53
72
|
|
|
54
73
|
const state = await device.getDeviceState();
|
|
55
74
|
|
|
56
75
|
expect(state.identity.serialNo).toBe('SERIAL-1');
|
|
57
|
-
expect(typedCall).toHaveBeenCalledTimes(
|
|
76
|
+
expect(typedCall).toHaveBeenCalledTimes(3);
|
|
58
77
|
expect(typedCall.mock.calls[0][0]).toBe('DeviceInfoGet');
|
|
59
78
|
expect(typedCall.mock.calls[0][2]?.targets?.status).not.toBe(true);
|
|
60
79
|
expect(typedCall).toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
|
|
61
80
|
});
|
|
62
81
|
|
|
63
82
|
test('uses one full DeviceInfoGet plus DeviceStatusGet when firmware refresh initializes state', async () => {
|
|
64
|
-
const typedCall = jest.fn().mockImplementation((requestType: string) =>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
84
|
+
if (requestType === 'DeviceInfoGet') {
|
|
85
|
+
return {
|
|
86
|
+
message: {
|
|
87
|
+
protocol_version: 2,
|
|
88
|
+
hw: { serial_no: 'SERIAL-1' },
|
|
89
|
+
fw: { application: { version: '5.0.0', hash: 'HASH-1' } },
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
94
|
+
return { message: protocolV2ApplicationInfo };
|
|
95
|
+
}
|
|
96
|
+
return { message: { init_states: true, unlocked: true } };
|
|
97
|
+
});
|
|
74
98
|
const device = createV2Device(typedCall);
|
|
75
99
|
|
|
76
100
|
await device.getDeviceState({
|
|
77
101
|
refreshSections: ['identity', 'versions', 'verification'],
|
|
78
102
|
});
|
|
79
103
|
|
|
80
|
-
expect(typedCall).toHaveBeenCalledTimes(
|
|
104
|
+
expect(typedCall).toHaveBeenCalledTimes(3);
|
|
81
105
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
82
106
|
1,
|
|
83
107
|
'DeviceInfoGet',
|
|
@@ -115,13 +139,13 @@ describe('getDeviceState', () => {
|
|
|
115
139
|
});
|
|
116
140
|
|
|
117
141
|
test.each(['bootloader', 'romloader'] as const)(
|
|
118
|
-
'
|
|
142
|
+
'uses ProtocolInfo to preserve %s mode without DeviceStatusGet',
|
|
119
143
|
async mode => {
|
|
120
144
|
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
121
145
|
if (requestType === 'DeviceInfoGet') {
|
|
122
146
|
return {
|
|
123
147
|
message: {
|
|
124
|
-
hw: { serial_no: 'SERIAL-1' },
|
|
148
|
+
hw: { Device_type: DeviceType.PRO2, serial_no: 'SERIAL-1' },
|
|
125
149
|
fw:
|
|
126
150
|
mode === 'romloader'
|
|
127
151
|
? { romloader: { version: '1.0.0' } }
|
|
@@ -129,24 +153,38 @@ describe('getDeviceState', () => {
|
|
|
129
153
|
},
|
|
130
154
|
};
|
|
131
155
|
}
|
|
132
|
-
|
|
156
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
157
|
+
return { message: getProtocolV2LoaderInfo(mode) };
|
|
158
|
+
}
|
|
159
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
133
160
|
});
|
|
134
161
|
const device = createV2Device(typedCall);
|
|
135
162
|
device.updateState({ protocol: 'V2', status: { mode } }, 'initialize');
|
|
136
163
|
|
|
137
164
|
const state = await device.getDeviceState({ refreshSections: ['status'] });
|
|
138
165
|
|
|
139
|
-
expect(state.status.mode).toBe(
|
|
166
|
+
expect(state.status.mode).toBe(mode);
|
|
140
167
|
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
141
168
|
'DeviceInfoGet',
|
|
142
|
-
'
|
|
169
|
+
'ProtocolInfoRequest',
|
|
143
170
|
]);
|
|
144
171
|
}
|
|
145
172
|
);
|
|
146
173
|
|
|
147
174
|
test('refreshes Protocol V2 status only when explicitly requested in normal mode', async () => {
|
|
148
|
-
const typedCall = jest.fn().
|
|
149
|
-
|
|
175
|
+
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
176
|
+
if (requestType === 'DeviceInfoGet') {
|
|
177
|
+
return {
|
|
178
|
+
message: {
|
|
179
|
+
hw: { serial_no: 'SERIAL-1' },
|
|
180
|
+
fw: { application: { version: '5.0.0' } },
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
185
|
+
return { message: protocolV2ApplicationInfo };
|
|
186
|
+
}
|
|
187
|
+
return { message: { init_states: true, unlocked: true, device_id: 'device-1' } };
|
|
150
188
|
});
|
|
151
189
|
const device = createV2Device(typedCall);
|
|
152
190
|
device.updateState({ protocol: 'V2', status: { mode: 'normal' } }, 'initialize');
|
|
@@ -203,6 +241,9 @@ describe('getDeviceState', () => {
|
|
|
203
241
|
message: { init_states: true, unlocked: false, device_id: 'device-1' },
|
|
204
242
|
};
|
|
205
243
|
}
|
|
244
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
245
|
+
return { message: protocolV2ApplicationInfo };
|
|
246
|
+
}
|
|
206
247
|
if (requestType === 'DeviceSettingsGet') {
|
|
207
248
|
return { message: { label: 'Renamed Pro 2' } };
|
|
208
249
|
}
|
|
@@ -215,6 +256,7 @@ describe('getDeviceState', () => {
|
|
|
215
256
|
|
|
216
257
|
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
217
258
|
'DeviceInfoGet',
|
|
259
|
+
'ProtocolInfoRequest',
|
|
218
260
|
'DeviceStatusGet',
|
|
219
261
|
'DeviceSettingsGet',
|
|
220
262
|
]);
|
|
@@ -237,6 +279,9 @@ describe('getDeviceState', () => {
|
|
|
237
279
|
message: { init_states: true, unlocked: false, device_id: 'device-1' },
|
|
238
280
|
};
|
|
239
281
|
}
|
|
282
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
283
|
+
return { message: protocolV2ApplicationInfo };
|
|
284
|
+
}
|
|
240
285
|
throw Object.assign(new Error('Device locked'), {
|
|
241
286
|
errorCode: HardwareErrorCode.DeviceLocked,
|
|
242
287
|
});
|
|
@@ -383,7 +428,10 @@ describe('getDeviceState', () => {
|
|
|
383
428
|
},
|
|
384
429
|
};
|
|
385
430
|
}
|
|
386
|
-
|
|
431
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
432
|
+
return { message: getProtocolV2LoaderInfo('bootloader') };
|
|
433
|
+
}
|
|
434
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
387
435
|
});
|
|
388
436
|
const device = createV2Device(typedCall);
|
|
389
437
|
device.updateState(
|
|
@@ -135,6 +135,15 @@ describe('KaspaSignTransaction capability flags', () => {
|
|
|
135
135
|
createMethod({ inputs: [buildInput()], outputs: [{ satoshis: 1 }] }).init()
|
|
136
136
|
).toThrow('outputs require either address/addressN');
|
|
137
137
|
});
|
|
138
|
+
|
|
139
|
+
it('rejects outputs containing both address and addressN', () => {
|
|
140
|
+
expect(() =>
|
|
141
|
+
createMethod({
|
|
142
|
+
inputs: [buildInput()],
|
|
143
|
+
outputs: [{ satoshis: 1, address: ADDRESS, addressN: CHANGE_PATH }],
|
|
144
|
+
}).init()
|
|
145
|
+
).toThrow('outputs must not contain both address and addressN');
|
|
146
|
+
});
|
|
138
147
|
});
|
|
139
148
|
|
|
140
149
|
describe('KaspaSignTransaction protocol negotiation', () => {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
UI_REQUEST,
|
|
3
|
+
UI_RESPONSE,
|
|
4
|
+
formatLogMethodLabel,
|
|
5
|
+
getLogBlockLabel,
|
|
6
|
+
getSafeLogPayload,
|
|
7
|
+
} from '../src/events';
|
|
2
8
|
|
|
3
9
|
describe('getLogBlockLabel', () => {
|
|
4
10
|
it('blocks evmSignTypedData params before logging large typed data', () => {
|
|
@@ -35,6 +41,21 @@ describe('getLogBlockLabel', () => {
|
|
|
35
41
|
expect(getLogBlockLabel({ type: UI_RESPONSE.RECEIVE_PIN })).toBe(UI_RESPONSE.RECEIVE_PIN);
|
|
36
42
|
});
|
|
37
43
|
|
|
44
|
+
it.each([UI_REQUEST.REQUEST_PASSPHRASE, UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE])(
|
|
45
|
+
'blocks wallet identifiers in %s event logging',
|
|
46
|
+
type => {
|
|
47
|
+
expect(
|
|
48
|
+
getLogBlockLabel({
|
|
49
|
+
type,
|
|
50
|
+
payload: {
|
|
51
|
+
passphraseState: 'wallet-identifier',
|
|
52
|
+
expectedPassphraseState: 'expected-wallet-identifier',
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
).toBe(type);
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
38
59
|
it('blocks openWalletSession wallet identifiers in direct and iframe call logging', () => {
|
|
39
60
|
const payload = {
|
|
40
61
|
method: 'openWalletSession',
|
|
@@ -53,16 +74,51 @@ describe('getLogBlockLabel', () => {
|
|
|
53
74
|
).toBe('openWalletSession');
|
|
54
75
|
});
|
|
55
76
|
|
|
77
|
+
it('keeps openWalletSession metadata while redacting wallet session identifiers', () => {
|
|
78
|
+
const safeResponse = getSafeLogPayload(
|
|
79
|
+
{
|
|
80
|
+
success: true,
|
|
81
|
+
payload: {
|
|
82
|
+
protocol: 'V2',
|
|
83
|
+
walletType: 'hidden',
|
|
84
|
+
deviceId: 'device-id',
|
|
85
|
+
passphraseState: 'wallet-identifier',
|
|
86
|
+
sessionId: 'wallet-session-id',
|
|
87
|
+
resumed: false,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
'openWalletSession'
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
expect(safeResponse).toEqual({
|
|
94
|
+
method: 'openWalletSession',
|
|
95
|
+
success: true,
|
|
96
|
+
payload: {
|
|
97
|
+
protocol: 'V2',
|
|
98
|
+
walletType: 'hidden',
|
|
99
|
+
deviceId: 'device-id',
|
|
100
|
+
passphraseState: '[REDACTED]',
|
|
101
|
+
sessionId: '[REDACTED]',
|
|
102
|
+
resumed: false,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
56
107
|
it.each(['deviceUploadWallpaper', 'uploadPortfolio', 'fileWrite', 'fileRead'])(
|
|
57
|
-
'
|
|
108
|
+
'keeps metadata and replaces binary payloads with their size for %s',
|
|
58
109
|
method => {
|
|
59
|
-
|
|
60
|
-
expect(getLogBlockLabel(
|
|
110
|
+
const request = { method, path: 'resource.bin', data: new Uint8Array(1024) };
|
|
111
|
+
expect(getLogBlockLabel(request)).toBe(method);
|
|
112
|
+
expect(getSafeLogPayload(request, method)).toEqual({
|
|
113
|
+
method,
|
|
114
|
+
path: 'resource.bin',
|
|
115
|
+
data: '[BINARY:1024]',
|
|
116
|
+
});
|
|
61
117
|
}
|
|
62
118
|
);
|
|
63
119
|
|
|
64
120
|
it.each(['evmSignMessage', 'btcSignMessage', 'evmSignTransaction'])(
|
|
65
|
-
'blocks request and response payload logging for
|
|
121
|
+
'blocks request and response payload logging for signing method %s',
|
|
66
122
|
method => {
|
|
67
123
|
expect(getLogBlockLabel({ method, message: 'sensitive signing payload' })).toBe(method);
|
|
68
124
|
expect(
|
|
@@ -74,6 +130,34 @@ describe('getLogBlockLabel', () => {
|
|
|
74
130
|
}
|
|
75
131
|
);
|
|
76
132
|
|
|
133
|
+
it('keeps ordinary API requests and responses visible', () => {
|
|
134
|
+
const request = { method: 'getDeviceState', connectId: 'connect-id', scope: 'runtime' };
|
|
135
|
+
const response = { success: true, payload: { protocol: 'V2', initialized: true } };
|
|
136
|
+
|
|
137
|
+
expect(getLogBlockLabel(request)).toBeUndefined();
|
|
138
|
+
expect(getSafeLogPayload(request)).toEqual(request);
|
|
139
|
+
expect(getSafeLogPayload(response)).toEqual(response);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('redacts sensitive keys even for ordinary API payloads', () => {
|
|
143
|
+
expect(
|
|
144
|
+
getSafeLogPayload({
|
|
145
|
+
method: 'ordinaryMethod',
|
|
146
|
+
payload: { session_id: 'session-secret', nested: { pin: '1234' } },
|
|
147
|
+
})
|
|
148
|
+
).toEqual({
|
|
149
|
+
method: 'ordinaryMethod',
|
|
150
|
+
payload: { session_id: '[REDACTED]', nested: { pin: '[REDACTED]' } },
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('puts sensitive API method names in the log label', () => {
|
|
155
|
+
expect(formatLogMethodLabel('response:', 'openWalletSession')).toBe(
|
|
156
|
+
'response: [openWalletSession]'
|
|
157
|
+
);
|
|
158
|
+
expect(formatLogMethodLabel('response:')).toBe('response:');
|
|
159
|
+
});
|
|
160
|
+
|
|
77
161
|
it('does not classify events without an API method as method calls', () => {
|
|
78
162
|
expect(getLogBlockLabel({ event: 'DEVICE_EVENT', type: 'device-connect' })).toBeUndefined();
|
|
79
163
|
});
|