@onekeyfe/hd-core 1.2.0-alpha.35 → 1.2.0-alpha.36
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__/AllNetworkGetAddressBase.tracing.test.ts +74 -0
- package/__tests__/DeviceEventRegistration.test.ts +6 -0
- package/__tests__/get-device-state.test.ts +104 -12
- package/__tests__/open-wallet-session.test.ts +131 -7
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +56 -0
- package/__tests__/protocol-v2.test.ts +290 -39
- 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/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
- package/dist/core/deviceEventRegistration.d.ts +2 -0
- package/dist/core/deviceEventRegistration.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +22 -4
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/events/device.d.ts +4 -0
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +27 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +56 -7
- package/dist/index.js +692 -397
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/uiInteraction.d.ts +3 -3
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +4 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +2 -0
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +1 -1
- package/src/api/FirmwareUpdateV4.ts +13 -6
- package/src/api/GetPassphraseState.ts +7 -1
- package/src/api/OpenWalletSession.ts +18 -3
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +24 -2
- package/src/api/device/DeviceUnlock.ts +1 -1
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
- package/src/core/deviceEventRegistration.ts +4 -0
- package/src/core/index.ts +46 -4
- package/src/data/messages/messages-protocol-v2.json +15 -0
- package/src/device/Device.ts +221 -24
- package/src/events/device.ts +4 -0
- package/src/events/ui-request.ts +32 -2
- package/src/protocols/protocol-v2/features.ts +9 -2
- package/src/protocols/protocol-v2/uiInteraction.ts +31 -5
- package/src/protocols/protocol-v2/unlockRetry.ts +33 -11
- package/src/protocols/protocol-v2/walletSession.ts +140 -32
- package/src/utils/deviceFeaturesUtils.ts +4 -0
package/src/events/ui-request.ts
CHANGED
|
@@ -56,6 +56,16 @@ export type ProtocolV2UiEventSource =
|
|
|
56
56
|
|
|
57
57
|
export type ProtocolV2UiCompletion = 'page-accepted' | 'operation-completed';
|
|
58
58
|
|
|
59
|
+
export type HardwareUiInteractionMeta = {
|
|
60
|
+
interactionId: string;
|
|
61
|
+
phaseId: string;
|
|
62
|
+
sequence: number;
|
|
63
|
+
phase: 'pin' | 'passphrase' | 'passphrase-on-device' | 'button' | 'processing';
|
|
64
|
+
transition: 'start' | 'complete' | 'finish';
|
|
65
|
+
outcome?: 'submitted' | 'succeeded' | 'failed' | 'cancelled' | 'disconnected';
|
|
66
|
+
protocol: 'V2';
|
|
67
|
+
};
|
|
68
|
+
|
|
59
69
|
export type ProtocolV2UiEventMetadata = {
|
|
60
70
|
source?: ProtocolV2UiEventSource;
|
|
61
71
|
reason?: string;
|
|
@@ -64,12 +74,29 @@ export type ProtocolV2UiEventMetadata = {
|
|
|
64
74
|
method?: string;
|
|
65
75
|
page?: string | number;
|
|
66
76
|
operation?: string;
|
|
77
|
+
interaction?: HardwareUiInteractionMeta;
|
|
67
78
|
};
|
|
68
79
|
|
|
80
|
+
export type UiRequestWindowClose =
|
|
81
|
+
| {
|
|
82
|
+
type: typeof UI_REQUEST.CLOSE_UI_WINDOW;
|
|
83
|
+
payload: HardwareUiInteractionMeta;
|
|
84
|
+
}
|
|
85
|
+
| {
|
|
86
|
+
type: typeof UI_REQUEST.CLOSE_UI_PIN_WINDOW;
|
|
87
|
+
payload: HardwareUiInteractionMeta;
|
|
88
|
+
}
|
|
89
|
+
| {
|
|
90
|
+
type: typeof UI_REQUEST.CLOSE_UI_WINDOW;
|
|
91
|
+
payload?: undefined;
|
|
92
|
+
}
|
|
93
|
+
| {
|
|
94
|
+
type: typeof UI_REQUEST.CLOSE_UI_PIN_WINDOW;
|
|
95
|
+
payload?: undefined;
|
|
96
|
+
};
|
|
97
|
+
|
|
69
98
|
export interface UiRequestWithoutPayload {
|
|
70
99
|
type:
|
|
71
|
-
| typeof UI_REQUEST.CLOSE_UI_WINDOW
|
|
72
|
-
| typeof UI_REQUEST.CLOSE_UI_PIN_WINDOW
|
|
73
100
|
| typeof UI_REQUEST.BLUETOOTH_PERMISSION
|
|
74
101
|
| typeof UI_REQUEST.BLUETOOTH_UNSUPPORTED
|
|
75
102
|
| typeof UI_REQUEST.BLUETOOTH_POWERED_OFF
|
|
@@ -111,6 +138,7 @@ export interface UiRequestPassphrase {
|
|
|
111
138
|
source?: 'wallet-session-coordinator';
|
|
112
139
|
reason?: 'open-wallet' | 'session-recovery';
|
|
113
140
|
expectedPassphraseState?: string;
|
|
141
|
+
interaction?: HardwareUiInteractionMeta;
|
|
114
142
|
};
|
|
115
143
|
}
|
|
116
144
|
|
|
@@ -121,6 +149,7 @@ export interface UiRequestPassphraseOnDevice {
|
|
|
121
149
|
passphraseState?: string;
|
|
122
150
|
source?: 'wallet-session-coordinator';
|
|
123
151
|
reason?: 'open-wallet' | 'session-recovery';
|
|
152
|
+
interaction?: HardwareUiInteractionMeta;
|
|
124
153
|
};
|
|
125
154
|
}
|
|
126
155
|
|
|
@@ -191,6 +220,7 @@ export interface PreviousAddressResult {
|
|
|
191
220
|
|
|
192
221
|
export type UiEvent =
|
|
193
222
|
| UiRequestWithoutPayload
|
|
223
|
+
| UiRequestWindowClose
|
|
194
224
|
| UiRequestDeviceAction
|
|
195
225
|
| UiRequestButton
|
|
196
226
|
| UiRequestPassphraseOnDevice
|
|
@@ -168,8 +168,15 @@ export async function requestProtocolV2ProtocolInfo({
|
|
|
168
168
|
}): Promise<ProtocolInfo> {
|
|
169
169
|
const response =
|
|
170
170
|
timeoutMs === undefined
|
|
171
|
-
? await commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
172
|
-
|
|
171
|
+
? await commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
172
|
+
eventless_wallet_session: true,
|
|
173
|
+
})
|
|
174
|
+
: await commands.typedCall(
|
|
175
|
+
'ProtocolInfoRequest',
|
|
176
|
+
'ProtocolInfo',
|
|
177
|
+
{ eventless_wallet_session: true },
|
|
178
|
+
{ timeoutMs }
|
|
179
|
+
);
|
|
173
180
|
return response.message;
|
|
174
181
|
}
|
|
175
182
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UI_REQUEST, createUiMessage } from '../../events';
|
|
2
2
|
|
|
3
|
-
import type { CoreMessage, ProtocolV2UiCompletion } from '../../events';
|
|
3
|
+
import type { CoreMessage, HardwareUiInteractionMeta, ProtocolV2UiCompletion } from '../../events';
|
|
4
4
|
import type { Device } from '../../device/Device';
|
|
5
5
|
import type { KnownDevice } from '../../types';
|
|
6
6
|
|
|
@@ -28,7 +28,15 @@ type ProtocolV2InteractionMethod = {
|
|
|
28
28
|
protocolV2UiInteraction?: ProtocolV2InteractionDescriptor;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
type InteractionDevice = Pick<Device, 'isProtocolV2' | 'toMessageObject'
|
|
31
|
+
type InteractionDevice = Pick<Device, 'isProtocolV2' | 'toMessageObject'> &
|
|
32
|
+
Partial<
|
|
33
|
+
Pick<
|
|
34
|
+
Device,
|
|
35
|
+
| 'createProtocolV2UiPhaseMetadata'
|
|
36
|
+
| 'finishProtocolV2UiInteraction'
|
|
37
|
+
| 'hasOpenProtocolV2UiInteraction'
|
|
38
|
+
>
|
|
39
|
+
>;
|
|
32
40
|
type PostMessage = (message: CoreMessage) => void;
|
|
33
41
|
|
|
34
42
|
export const isProtocolV2UiEnabled = (method: { protocolV2UiMode?: 'auto' | 'none' }) =>
|
|
@@ -156,12 +164,17 @@ export class ProtocolV2UiInteractionCoordinator {
|
|
|
156
164
|
this.methodInteraction = interaction;
|
|
157
165
|
if (!interaction) return;
|
|
158
166
|
const { request, ...metadata } = interaction;
|
|
167
|
+
const interactionMeta = this.device.createProtocolV2UiPhaseMetadata?.(
|
|
168
|
+
request === 'pin' ? 'pin' : 'button',
|
|
169
|
+
'start'
|
|
170
|
+
);
|
|
159
171
|
|
|
160
172
|
this.emit(
|
|
161
173
|
request === 'pin' ? UI_REQUEST.REQUEST_PIN : UI_REQUEST.REQUEST_BUTTON,
|
|
162
174
|
{
|
|
163
175
|
device: this.device.toMessageObject() as KnownDevice,
|
|
164
176
|
...metadata,
|
|
177
|
+
...(interactionMeta ? { interaction: interactionMeta } : {}),
|
|
165
178
|
},
|
|
166
179
|
`method:${request}:${interaction.reason}:${interaction.page ?? ''}:${
|
|
167
180
|
interaction.operation ?? ''
|
|
@@ -169,7 +182,8 @@ export class ProtocolV2UiInteractionCoordinator {
|
|
|
169
182
|
);
|
|
170
183
|
}
|
|
171
184
|
|
|
172
|
-
enterUnlockInteraction(method?: string) {
|
|
185
|
+
enterUnlockInteraction(method?: string): HardwareUiInteractionMeta | undefined {
|
|
186
|
+
const interaction = this.device.createProtocolV2UiPhaseMetadata?.('pin', 'start');
|
|
173
187
|
this.emit(
|
|
174
188
|
UI_REQUEST.REQUEST_PIN,
|
|
175
189
|
{
|
|
@@ -178,9 +192,11 @@ export class ProtocolV2UiInteractionCoordinator {
|
|
|
178
192
|
reason: 'device-locked',
|
|
179
193
|
deviceOnly: true,
|
|
180
194
|
method,
|
|
195
|
+
...(interaction ? { interaction } : {}),
|
|
181
196
|
},
|
|
182
197
|
`unlock:${method ?? ''}`
|
|
183
198
|
);
|
|
199
|
+
return interaction;
|
|
184
200
|
}
|
|
185
201
|
|
|
186
202
|
resumeMethodInteraction() {
|
|
@@ -188,9 +204,19 @@ export class ProtocolV2UiInteractionCoordinator {
|
|
|
188
204
|
}
|
|
189
205
|
|
|
190
206
|
close() {
|
|
191
|
-
if (
|
|
207
|
+
if (
|
|
208
|
+
!this.device.isProtocolV2() ||
|
|
209
|
+
(!this.opened && !this.device.hasOpenProtocolV2UiInteraction?.()) ||
|
|
210
|
+
this.closed
|
|
211
|
+
)
|
|
212
|
+
return;
|
|
192
213
|
this.closed = true;
|
|
193
|
-
this.
|
|
214
|
+
const interaction = this.device.finishProtocolV2UiInteraction?.();
|
|
215
|
+
this.postMessage(
|
|
216
|
+
interaction
|
|
217
|
+
? createUiMessage(UI_REQUEST.CLOSE_UI_WINDOW, interaction)
|
|
218
|
+
: createUiMessage(UI_REQUEST.CLOSE_UI_WINDOW)
|
|
219
|
+
);
|
|
194
220
|
}
|
|
195
221
|
|
|
196
222
|
private emit(
|
|
@@ -5,6 +5,7 @@ import { refreshProtocolV2DeviceStatus, restoreProtocolV2WalletSession } from '.
|
|
|
5
5
|
|
|
6
6
|
import type { BaseMethod } from '../../api/BaseMethod';
|
|
7
7
|
import type { Device } from '../../device/Device';
|
|
8
|
+
import type { HardwareUiInteractionMeta } from '../../events';
|
|
8
9
|
import type { ProtocolV2UiInteractionCoordinator } from './uiInteraction';
|
|
9
10
|
|
|
10
11
|
const Log = getLogger(LoggerNames.Core);
|
|
@@ -46,6 +47,7 @@ export async function runMethodWithUnlockRetry(
|
|
|
46
47
|
uiCoordinator?: UiInteractionCoordinator
|
|
47
48
|
) {
|
|
48
49
|
const shouldEmitUi = isProtocolV2UiEnabled(method);
|
|
50
|
+
const shouldCoordinateUi = shouldEmitUi && uiCoordinator !== undefined;
|
|
49
51
|
const isProtocolV2 = device.isProtocolV2();
|
|
50
52
|
const requiresFreshStatus =
|
|
51
53
|
isProtocolV2 &&
|
|
@@ -65,19 +67,29 @@ export async function runMethodWithUnlockRetry(
|
|
|
65
67
|
device.features?.unlocked === false;
|
|
66
68
|
|
|
67
69
|
if (shouldUnlockBeforeRun) {
|
|
68
|
-
|
|
69
|
-
uiCoordinator?.enterUnlockInteraction(method.name)
|
|
70
|
-
|
|
71
|
-
await device.unlockDevice(
|
|
70
|
+
const unlockInteraction: HardwareUiInteractionMeta | undefined = shouldCoordinateUi
|
|
71
|
+
? uiCoordinator?.enterUnlockInteraction(method.name)
|
|
72
|
+
: undefined;
|
|
73
|
+
await device.unlockDevice(
|
|
74
|
+
undefined,
|
|
75
|
+
shouldCoordinateUi
|
|
76
|
+
? { emitUiEvent: false, interaction: unlockInteraction }
|
|
77
|
+
: {
|
|
78
|
+
source: 'unlock-coordinator',
|
|
79
|
+
reason: 'device-locked',
|
|
80
|
+
deviceOnly: true,
|
|
81
|
+
method: method.name,
|
|
82
|
+
}
|
|
83
|
+
);
|
|
72
84
|
Log.debug('Protocol V2 pre-unlock completed', { method: method.name });
|
|
73
85
|
await restoreExpectedWalletSessionAfterUnlock(method, device);
|
|
74
|
-
if (
|
|
86
|
+
if (shouldCoordinateUi) {
|
|
75
87
|
uiCoordinator?.enterMethodInteraction(resolveProtocolV2UiInteraction(method));
|
|
76
88
|
}
|
|
77
89
|
return method.run();
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
if (
|
|
92
|
+
if (shouldCoordinateUi) {
|
|
81
93
|
uiCoordinator?.enterMethodInteraction(resolveProtocolV2UiInteraction(method));
|
|
82
94
|
}
|
|
83
95
|
try {
|
|
@@ -88,13 +100,23 @@ export async function runMethodWithUnlockRetry(
|
|
|
88
100
|
}
|
|
89
101
|
|
|
90
102
|
Log.debug('Protocol V2 unlock retry triggered', { method: method.name });
|
|
91
|
-
|
|
92
|
-
uiCoordinator?.enterUnlockInteraction(method.name)
|
|
93
|
-
|
|
94
|
-
await device.unlockDevice(
|
|
103
|
+
const unlockInteraction: HardwareUiInteractionMeta | undefined = shouldCoordinateUi
|
|
104
|
+
? uiCoordinator?.enterUnlockInteraction(method.name)
|
|
105
|
+
: undefined;
|
|
106
|
+
await device.unlockDevice(
|
|
107
|
+
undefined,
|
|
108
|
+
shouldCoordinateUi
|
|
109
|
+
? { emitUiEvent: false, interaction: unlockInteraction }
|
|
110
|
+
: {
|
|
111
|
+
source: 'unlock-coordinator',
|
|
112
|
+
reason: 'device-locked',
|
|
113
|
+
deviceOnly: true,
|
|
114
|
+
method: method.name,
|
|
115
|
+
}
|
|
116
|
+
);
|
|
95
117
|
Log.debug('Protocol V2 unlock completed', { method: method.name });
|
|
96
118
|
await restoreExpectedWalletSessionAfterUnlock(method, device);
|
|
97
|
-
if (
|
|
119
|
+
if (shouldCoordinateUi) {
|
|
98
120
|
uiCoordinator?.resumeMethodInteraction();
|
|
99
121
|
}
|
|
100
122
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
-
import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
2
|
+
import { DeviceSessionPinType, DeviceSessionSeedDomain } from '@onekeyfe/hd-transport';
|
|
3
3
|
|
|
4
4
|
import { DEVICE } from '../../events';
|
|
5
5
|
import { assertCompleteDeviceSession } from './deviceSession';
|
|
@@ -38,32 +38,69 @@ export async function refreshProtocolV2DeviceStatus(device: Device) {
|
|
|
38
38
|
return device.updateProtocolV2Status(status);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
export async function ensureProtocolV2WalletSessionUnlocked(device: Device) {
|
|
42
|
+
if (!device.isProtocolV2() || device.isBootloader?.() || device.isRomloader?.()) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await refreshProtocolV2DeviceStatus(device);
|
|
47
|
+
if (device.state?.status.unlocked !== false) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await device.unlockDevice(DeviceSessionPinType.Main, {
|
|
52
|
+
source: 'unlock-coordinator',
|
|
53
|
+
reason: 'device-locked',
|
|
54
|
+
deviceOnly: true,
|
|
44
55
|
});
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const negotiateEventlessWalletSession = async (device: Device) => {
|
|
60
|
+
await device.ensureProtocolV2RuntimeContext();
|
|
45
61
|
};
|
|
46
62
|
|
|
47
|
-
const getDeviceSession = async (
|
|
48
|
-
device
|
|
63
|
+
const getDeviceSession = async (
|
|
64
|
+
device: Device,
|
|
65
|
+
request: DeviceSessionGet,
|
|
66
|
+
deriveCardano?: boolean
|
|
67
|
+
) => {
|
|
68
|
+
const payload =
|
|
69
|
+
deriveCardano === undefined
|
|
70
|
+
? request
|
|
71
|
+
: {
|
|
72
|
+
...request,
|
|
73
|
+
seed_domains: [
|
|
74
|
+
DeviceSessionSeedDomain.SeedDomain_Standard,
|
|
75
|
+
...(deriveCardano ? [DeviceSessionSeedDomain.SeedDomain_Cardano] : []),
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
return device.commands.typedCall('DeviceSessionGet', 'DeviceSession', payload);
|
|
79
|
+
};
|
|
49
80
|
|
|
50
81
|
const askDevicePassphrase = async (device: Device, requestPayload: DeviceSessionAskPassphrase) => {
|
|
51
82
|
await device.commands.typedCall('DeviceSessionAskPassphrase', 'Success', requestPayload);
|
|
52
83
|
await refreshProtocolV2DeviceStatus(device);
|
|
53
84
|
};
|
|
54
85
|
|
|
55
|
-
const selectDeviceSession = async (
|
|
86
|
+
const selectDeviceSession = async (
|
|
87
|
+
device: Device,
|
|
88
|
+
expectedPassphraseState?: string,
|
|
89
|
+
deriveCardano?: boolean
|
|
90
|
+
) => {
|
|
56
91
|
const existsAttachPinUser = device.features?.attachToPinEnabled === true;
|
|
57
92
|
const metadata = {
|
|
58
93
|
source: 'wallet-session-coordinator' as const,
|
|
59
94
|
reason: expectedPassphraseState ? ('session-recovery' as const) : ('open-wallet' as const),
|
|
60
95
|
...(expectedPassphraseState ? { expectedPassphraseState } : {}),
|
|
61
96
|
};
|
|
97
|
+
const passphraseInteraction = device.createProtocolV2UiPhaseMetadata?.('passphrase', 'start');
|
|
62
98
|
const response = await device.commands.promptPassphrase(
|
|
63
99
|
{
|
|
64
100
|
existsAttachPinUser,
|
|
65
101
|
deviceOnly: false,
|
|
66
102
|
...metadata,
|
|
103
|
+
...(passphraseInteraction ? { interaction: passphraseInteraction } : {}),
|
|
67
104
|
},
|
|
68
105
|
{ cancelDeviceOnReject: false }
|
|
69
106
|
);
|
|
@@ -102,9 +139,16 @@ const selectDeviceSession = async (device: Device, expectedPassphraseState?: str
|
|
|
102
139
|
'Attach PIN wallet selection is unavailable on this device.'
|
|
103
140
|
);
|
|
104
141
|
}
|
|
105
|
-
device.
|
|
106
|
-
|
|
107
|
-
|
|
142
|
+
const attachPinInteraction = device.createProtocolV2UiPhaseMetadata?.('pin', 'start');
|
|
143
|
+
device.emit(DEVICE.ATTACH_PIN_ON_DEVICE, device, {
|
|
144
|
+
...metadata,
|
|
145
|
+
...(attachPinInteraction ? { interaction: attachPinInteraction } : {}),
|
|
146
|
+
});
|
|
147
|
+
await device.unlockDevice(DeviceSessionPinType.AttachToPin, {
|
|
148
|
+
emitUiEvent: false,
|
|
149
|
+
interaction: attachPinInteraction,
|
|
150
|
+
});
|
|
151
|
+
return getDeviceSession(device, {}, deriveCardano);
|
|
108
152
|
}
|
|
109
153
|
|
|
110
154
|
if (hasHostPassphrase) {
|
|
@@ -112,12 +156,15 @@ const selectDeviceSession = async (device: Device, expectedPassphraseState?: str
|
|
|
112
156
|
passphrase: hostPassphrase,
|
|
113
157
|
on_device: false,
|
|
114
158
|
});
|
|
115
|
-
return getDeviceSession(device, {});
|
|
159
|
+
return getDeviceSession(device, {}, deriveCardano);
|
|
116
160
|
}
|
|
117
161
|
|
|
118
|
-
device.emit(DEVICE.PASSPHRASE_ON_DEVICE, device,
|
|
162
|
+
device.emit(DEVICE.PASSPHRASE_ON_DEVICE, device, {
|
|
163
|
+
...metadata,
|
|
164
|
+
...(passphraseInteraction ? { interaction: passphraseInteraction } : {}),
|
|
165
|
+
});
|
|
119
166
|
await askDevicePassphrase(device, { on_device: true });
|
|
120
|
-
return getDeviceSession(device, {});
|
|
167
|
+
return getDeviceSession(device, {}, deriveCardano);
|
|
121
168
|
};
|
|
122
169
|
|
|
123
170
|
export async function getProtocolV2WalletSession(
|
|
@@ -128,7 +175,9 @@ export async function getProtocolV2WalletSession(
|
|
|
128
175
|
initSession?: boolean;
|
|
129
176
|
expectedPassphraseState?: string;
|
|
130
177
|
onlyMainPin?: boolean;
|
|
178
|
+
selectMainWalletBeforeRestore?: boolean;
|
|
131
179
|
resumeOnly?: boolean;
|
|
180
|
+
deriveCardano?: boolean;
|
|
132
181
|
}
|
|
133
182
|
) {
|
|
134
183
|
const forceWalletSelection =
|
|
@@ -161,17 +210,57 @@ export async function getProtocolV2WalletSession(
|
|
|
161
210
|
: undefined;
|
|
162
211
|
let response;
|
|
163
212
|
let resumed = false;
|
|
213
|
+
let mainWalletSelected = false;
|
|
214
|
+
|
|
215
|
+
const clearCurrentWalletSession = () => {
|
|
216
|
+
if (options?.onlyMainPin) {
|
|
217
|
+
device.clearStandardInternalState?.();
|
|
218
|
+
} else {
|
|
219
|
+
device.clearInternalState();
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const rejectMismatchedAttachPinWallet = async () => {
|
|
224
|
+
const features = await refreshProtocolV2DeviceStatus(device);
|
|
225
|
+
if (features.unlockedAttachPin !== true) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
await device.lockDevice();
|
|
231
|
+
} catch {
|
|
232
|
+
// Reject the mismatched Attach PIN wallet even when older firmware cannot lock.
|
|
233
|
+
}
|
|
234
|
+
clearCurrentWalletSession();
|
|
235
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckUnlockTypeError);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const selectMainWallet = async (force = false) => {
|
|
239
|
+
if (force || !mainWalletSelected) {
|
|
240
|
+
await device.unlockDevice(DeviceSessionPinType.Main, {
|
|
241
|
+
source: 'wallet-session-coordinator',
|
|
242
|
+
reason: expectedPassphraseState ? 'session-recovery' : 'open-wallet',
|
|
243
|
+
deviceOnly: true,
|
|
244
|
+
});
|
|
245
|
+
mainWalletSelected = true;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
164
248
|
|
|
165
249
|
if (options?.onlyMainPin) {
|
|
166
250
|
expectedPassphraseState = cachedStandardSession?.passphraseState;
|
|
167
251
|
if (cachedStandardSession) {
|
|
168
252
|
try {
|
|
169
|
-
if (
|
|
170
|
-
await
|
|
253
|
+
if (options?.selectMainWalletBeforeRestore) {
|
|
254
|
+
await selectMainWallet();
|
|
171
255
|
}
|
|
172
|
-
response = await getDeviceSession(
|
|
173
|
-
|
|
174
|
-
|
|
256
|
+
response = await getDeviceSession(
|
|
257
|
+
device,
|
|
258
|
+
{
|
|
259
|
+
session_id: cachedStandardSession.sessionId,
|
|
260
|
+
btc_test_address: cachedStandardSession.passphraseState,
|
|
261
|
+
},
|
|
262
|
+
options?.deriveCardano
|
|
263
|
+
);
|
|
175
264
|
resumed = true;
|
|
176
265
|
} catch (error) {
|
|
177
266
|
device.clearStandardInternalState?.();
|
|
@@ -183,12 +272,19 @@ export async function getProtocolV2WalletSession(
|
|
|
183
272
|
}
|
|
184
273
|
|
|
185
274
|
if (!response) {
|
|
186
|
-
await
|
|
187
|
-
response = await getDeviceSession(device, {});
|
|
275
|
+
await selectMainWallet();
|
|
276
|
+
response = await getDeviceSession(device, {}, options?.deriveCardano);
|
|
188
277
|
}
|
|
189
278
|
} else if (cachedSessionId && expectedPassphraseState) {
|
|
190
279
|
try {
|
|
191
|
-
response = await getDeviceSession(
|
|
280
|
+
response = await getDeviceSession(
|
|
281
|
+
device,
|
|
282
|
+
{
|
|
283
|
+
session_id: cachedSessionId,
|
|
284
|
+
btc_test_address: expectedPassphraseState,
|
|
285
|
+
},
|
|
286
|
+
options?.deriveCardano
|
|
287
|
+
);
|
|
192
288
|
resumed = true;
|
|
193
289
|
} catch (error) {
|
|
194
290
|
device.clearInternalState();
|
|
@@ -197,6 +293,18 @@ export async function getProtocolV2WalletSession(
|
|
|
197
293
|
}
|
|
198
294
|
resumed = false;
|
|
199
295
|
}
|
|
296
|
+
} else if (expectedPassphraseState) {
|
|
297
|
+
try {
|
|
298
|
+
response = await getDeviceSession(
|
|
299
|
+
device,
|
|
300
|
+
{ btc_test_address: expectedPassphraseState },
|
|
301
|
+
options?.deriveCardano
|
|
302
|
+
);
|
|
303
|
+
} catch (error) {
|
|
304
|
+
if (options?.resumeOnly || !isWalletSessionInvalidError(error)) {
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
200
308
|
}
|
|
201
309
|
|
|
202
310
|
if (!response) {
|
|
@@ -204,7 +312,7 @@ export async function getProtocolV2WalletSession(
|
|
|
204
312
|
device.clearInternalState();
|
|
205
313
|
throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
|
|
206
314
|
}
|
|
207
|
-
response = await selectDeviceSession(device, expectedPassphraseState);
|
|
315
|
+
response = await selectDeviceSession(device, expectedPassphraseState, options?.deriveCardano);
|
|
208
316
|
}
|
|
209
317
|
|
|
210
318
|
let { message } = response;
|
|
@@ -220,17 +328,18 @@ export async function getProtocolV2WalletSession(
|
|
|
220
328
|
}
|
|
221
329
|
if (expectedPassphraseState && expectedPassphraseState !== message.btc_test_address) {
|
|
222
330
|
resumed = false;
|
|
331
|
+
await rejectMismatchedAttachPinWallet();
|
|
223
332
|
if (options?.resumeOnly) {
|
|
224
333
|
device.clearInternalState();
|
|
225
334
|
throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
|
|
226
335
|
}
|
|
227
336
|
if (options?.onlyMainPin) {
|
|
228
337
|
device.clearStandardInternalState?.();
|
|
229
|
-
await
|
|
230
|
-
response = await getDeviceSession(device, {});
|
|
338
|
+
await selectMainWallet(true);
|
|
339
|
+
response = await getDeviceSession(device, {}, options?.deriveCardano);
|
|
231
340
|
} else {
|
|
232
341
|
device.clearInternalState();
|
|
233
|
-
response = await selectDeviceSession(device, expectedPassphraseState);
|
|
342
|
+
response = await selectDeviceSession(device, expectedPassphraseState, options?.deriveCardano);
|
|
234
343
|
}
|
|
235
344
|
message = response.message;
|
|
236
345
|
try {
|
|
@@ -244,11 +353,8 @@ export async function getProtocolV2WalletSession(
|
|
|
244
353
|
throw error;
|
|
245
354
|
}
|
|
246
355
|
if (expectedPassphraseState !== message.btc_test_address) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
} else {
|
|
250
|
-
device.clearInternalState();
|
|
251
|
-
}
|
|
356
|
+
await rejectMismatchedAttachPinWallet();
|
|
357
|
+
clearCurrentWalletSession();
|
|
252
358
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
|
|
253
359
|
}
|
|
254
360
|
}
|
|
@@ -269,17 +375,19 @@ export async function getProtocolV2WalletSession(
|
|
|
269
375
|
return {
|
|
270
376
|
passphraseState: message.btc_test_address,
|
|
271
377
|
newSession: message.session_id,
|
|
272
|
-
unlockedAttachPin:
|
|
378
|
+
unlockedAttachPin: mainWalletSelected ? false : undefined,
|
|
273
379
|
resumed,
|
|
274
380
|
};
|
|
275
381
|
}
|
|
276
382
|
|
|
277
383
|
export async function restoreProtocolV2WalletSession(
|
|
278
384
|
device: Device,
|
|
279
|
-
expectedPassphraseState: string
|
|
385
|
+
expectedPassphraseState: string,
|
|
386
|
+
deriveCardano?: boolean
|
|
280
387
|
) {
|
|
281
388
|
return getProtocolV2WalletSession(device, {
|
|
282
389
|
expectedPassphraseState,
|
|
283
390
|
resumeOnly: true,
|
|
391
|
+
deriveCardano,
|
|
284
392
|
});
|
|
285
393
|
}
|
|
@@ -106,6 +106,7 @@ export const getPassphraseStateWithRefreshDeviceInfo = async (
|
|
|
106
106
|
expectPassphraseState?: string;
|
|
107
107
|
onlyMainPin?: boolean;
|
|
108
108
|
initSession?: boolean;
|
|
109
|
+
deriveCardano?: boolean;
|
|
109
110
|
}
|
|
110
111
|
) => {
|
|
111
112
|
if (device.isProtocolV2()) {
|
|
@@ -121,6 +122,7 @@ export const getPassphraseStateWithRefreshDeviceInfo = async (
|
|
|
121
122
|
initSession: options?.initSession,
|
|
122
123
|
expectedPassphraseState: options?.expectPassphraseState,
|
|
123
124
|
onlyMainPin: options?.onlyMainPin,
|
|
125
|
+
deriveCardano: options?.deriveCardano,
|
|
124
126
|
});
|
|
125
127
|
}
|
|
126
128
|
|
|
@@ -185,6 +187,7 @@ export const getPassphraseState = async (
|
|
|
185
187
|
expectPassphraseState?: string;
|
|
186
188
|
onlyMainPin?: boolean;
|
|
187
189
|
initSession?: boolean;
|
|
190
|
+
deriveCardano?: boolean;
|
|
188
191
|
}
|
|
189
192
|
): Promise<{
|
|
190
193
|
passphraseState: string | undefined;
|
|
@@ -205,6 +208,7 @@ export const getPassphraseState = async (
|
|
|
205
208
|
initSession: options?.initSession,
|
|
206
209
|
expectedPassphraseState: options?.expectPassphraseState,
|
|
207
210
|
onlyMainPin: options?.onlyMainPin,
|
|
211
|
+
deriveCardano: options?.deriveCardano,
|
|
208
212
|
});
|
|
209
213
|
}
|
|
210
214
|
|