@onekeyfe/hd-core 1.2.2-alpha.100 → 1.2.2-alpha.12
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 +20 -4
- package/__tests__/DeviceCommands.test.ts +13 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
- package/__tests__/check-firmware-release-protocol-v2.test.ts +0 -2
- package/__tests__/device-lifecycle-events.test.ts +86 -0
- package/__tests__/deviceUploadNft.test.ts +68 -0
- package/__tests__/evmSignTypedData.test.ts +42 -0
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +825 -12
- package/__tests__/logBlockEvent.test.ts +45 -122
- package/__tests__/open-wallet-session.test.ts +781 -69
- package/__tests__/pro2HostAssetPackage.test.ts +165 -0
- package/__tests__/protocol-v2-resources.test.ts +7 -4
- package/__tests__/protocol-v2-unlock-policy.test.ts +35 -1
- package/__tests__/protocol-v2.test.ts +925 -97
- package/__tests__/protocolV2FileWrite.test.ts +40 -0
- package/dist/api/FirmwareUpdateV4.d.ts +5 -0
- package/dist/api/FirmwareUpdateV4.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/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +2 -2
- package/dist/api/helpers/protocolV2FileWrite.d.ts +1 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts +2 -0
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +1 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +3 -0
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +21 -5
- package/dist/index.js +2511 -1538
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +2 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +3 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/settings.d.ts +2 -2
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +3 -0
- 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/pro2HostAssetPackage.d.ts +8 -0
- package/dist/utils/pro2HostAssetPackage.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +474 -83
- package/src/api/OpenWalletSession.ts +39 -12
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +5 -2
- package/src/api/evm/EVMSignTypedData.ts +1 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +9 -3
- package/src/api/helpers/protocolV2FileWrite.ts +11 -5
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +7 -1
- package/src/api/protocol-v2/DeviceUploadNft.ts +31 -8
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +33 -8
- package/src/api/protocol-v2/helpers.ts +8 -0
- package/src/core/RequestQueue.ts +16 -1
- package/src/core/index.ts +164 -26
- package/src/data/messages/messages-protocol-v2.json +1589 -1375
- package/src/device/Device.ts +3 -1
- package/src/device/DeviceConnector.ts +7 -3
- package/src/events/logBlockEvent.ts +6 -75
- package/src/events/ui-request.ts +3 -0
- package/src/protocols/protocol-v2/resources.ts +9 -1
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +8 -1
- package/src/protocols/protocol-v2/walletSession.ts +223 -56
- package/src/types/settings.ts +4 -2
- package/src/utils/deviceFeaturesUtils.ts +4 -0
- package/src/utils/pro2HostAssetPackage.ts +400 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/* eslint-disable no-bitwise -- LZ4 decoding and deterministic test data generation require bitwise operations. */
|
|
2
|
+
import { sha3_512 } from '@noble/hashes/sha3';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
buildPro2HostAssetPackage,
|
|
6
|
+
supportsPro2HostAssetPackage,
|
|
7
|
+
} from '../src/utils/pro2HostAssetPackage';
|
|
8
|
+
|
|
9
|
+
const decodeRawLz4Block = (compressed: Uint8Array, expectedLength: number) => {
|
|
10
|
+
const output = new Uint8Array(expectedLength);
|
|
11
|
+
let inputOffset = 0;
|
|
12
|
+
let outputOffset = 0;
|
|
13
|
+
|
|
14
|
+
const readLength = (initialLength: number) => {
|
|
15
|
+
let length = initialLength;
|
|
16
|
+
if (length === 15) {
|
|
17
|
+
let extension = 255;
|
|
18
|
+
while (extension === 255) {
|
|
19
|
+
extension = compressed[inputOffset];
|
|
20
|
+
inputOffset += 1;
|
|
21
|
+
length += extension;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return length;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
while (inputOffset < compressed.byteLength) {
|
|
28
|
+
const token = compressed[inputOffset];
|
|
29
|
+
inputOffset += 1;
|
|
30
|
+
const literalLength = readLength(token >>> 4);
|
|
31
|
+
output.set(compressed.subarray(inputOffset, inputOffset + literalLength), outputOffset);
|
|
32
|
+
inputOffset += literalLength;
|
|
33
|
+
outputOffset += literalLength;
|
|
34
|
+
if (inputOffset >= compressed.byteLength) break;
|
|
35
|
+
|
|
36
|
+
const matchOffset = compressed[inputOffset] | (compressed[inputOffset + 1] << 8);
|
|
37
|
+
inputOffset += 2;
|
|
38
|
+
const matchLength = readLength(token & 0x0f) + 4;
|
|
39
|
+
for (let index = 0; index < matchLength; index += 1) {
|
|
40
|
+
output[outputOffset] = output[outputOffset - matchOffset];
|
|
41
|
+
outputOffset += 1;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
expect(outputOffset).toBe(expectedLength);
|
|
46
|
+
return output;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const decodeFirstPackageEntry = (packageData: Uint8Array, rawLength: number) => {
|
|
50
|
+
const containerHeaderSize = 0x5f90;
|
|
51
|
+
const archive = packageData.subarray(containerHeaderSize);
|
|
52
|
+
const archiveView = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
|
|
53
|
+
const compressedOffset = archiveView.getUint32(42 + 0x100, true);
|
|
54
|
+
const compressed = archive.subarray(compressedOffset);
|
|
55
|
+
const compressedView = new DataView(
|
|
56
|
+
compressed.buffer,
|
|
57
|
+
compressed.byteOffset,
|
|
58
|
+
compressed.byteLength
|
|
59
|
+
);
|
|
60
|
+
const blockCount = compressedView.getUint16(0, true);
|
|
61
|
+
const blockSize = 1 << compressedView.getUint16(2, true);
|
|
62
|
+
let blockOffset = 8 + blockCount * 4;
|
|
63
|
+
const decodedBlocks: Uint8Array[] = [];
|
|
64
|
+
|
|
65
|
+
for (let index = 0; index < blockCount; index += 1) {
|
|
66
|
+
const compressedLength = compressedView.getUint32(8 + index * 4, true);
|
|
67
|
+
const expectedLength = Math.min(blockSize, rawLength - index * blockSize);
|
|
68
|
+
decodedBlocks.push(
|
|
69
|
+
decodeRawLz4Block(
|
|
70
|
+
compressed.subarray(blockOffset, blockOffset + compressedLength),
|
|
71
|
+
expectedLength
|
|
72
|
+
)
|
|
73
|
+
);
|
|
74
|
+
blockOffset += compressedLength;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const decoded = new Uint8Array(rawLength);
|
|
78
|
+
decodedBlocks.reduce((offset, block) => {
|
|
79
|
+
decoded.set(block, offset);
|
|
80
|
+
return offset + block.byteLength;
|
|
81
|
+
}, 0);
|
|
82
|
+
return decoded;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
describe('Pro2 host asset package', () => {
|
|
86
|
+
test('builds the unsigned RESOURCE container and LZ4-blocked archive expected by firmware', () => {
|
|
87
|
+
const raw = new TextEncoder().encode('123456789');
|
|
88
|
+
const packageData = buildPro2HostAssetPackage([{ name: 'wallpaper.bin', data: raw }]);
|
|
89
|
+
const headerSize = 0x5f90;
|
|
90
|
+
const payload = packageData.subarray(headerSize);
|
|
91
|
+
const packageView = new DataView(
|
|
92
|
+
packageData.buffer,
|
|
93
|
+
packageData.byteOffset,
|
|
94
|
+
packageData.byteLength
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(packageView.getUint32(0, true)).toBe(0x50504b4f);
|
|
98
|
+
expect(packageView.getUint32(4, true)).toBe(1);
|
|
99
|
+
expect(packageView.getUint32(8, true)).toBe(0x43534552);
|
|
100
|
+
expect(packageView.getUint32(0x0c, true)).toBe(headerSize);
|
|
101
|
+
expect(packageView.getUint32(0x10, true)).toBe(1);
|
|
102
|
+
expect(packageView.getUint32(0x14, true)).toBe(payload.byteLength);
|
|
103
|
+
expect(packageData.subarray(0x200, 0x240)).toEqual(sha3_512(payload));
|
|
104
|
+
expect(packageData.subarray(0x240, 0x280)).toEqual(sha3_512(packageData.subarray(0, 0x240)));
|
|
105
|
+
expect(packageData[0x400]).toBe(0);
|
|
106
|
+
expect(packageView.getUint32(0x408, true)).toBe(0x71717171);
|
|
107
|
+
|
|
108
|
+
const archiveView = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
109
|
+
expect(archiveView.getUint32(0, true)).toBe(0x52414b4f);
|
|
110
|
+
expect(archiveView.getUint32(4, true)).toBe(1);
|
|
111
|
+
expect(archiveView.getUint16(8, true)).toBe(1);
|
|
112
|
+
expect(new TextDecoder().decode(payload.subarray(43, 56))).toBe('wallpaper.bin');
|
|
113
|
+
expect(archiveView.getUint32(42 + 0x100, true)).toBe(340);
|
|
114
|
+
expect(archiveView.getUint32(42 + 0x104, true)).toBe(raw.byteLength);
|
|
115
|
+
expect(archiveView.getUint32(42 + 0x10c, true)).toBe(0xcbf43926);
|
|
116
|
+
expect(payload[42 + 0x114]).toBe(1);
|
|
117
|
+
|
|
118
|
+
const compressedOffset = archiveView.getUint32(42 + 0x100, true);
|
|
119
|
+
expect(archiveView.getUint16(compressedOffset, true)).toBe(1);
|
|
120
|
+
expect(archiveView.getUint16(compressedOffset + 2, true)).toBe(14);
|
|
121
|
+
expect(archiveView.getUint32(compressedOffset + 4, true)).toBe(0);
|
|
122
|
+
expect(archiveView.getUint32(compressedOffset + 8, true)).toBe(10);
|
|
123
|
+
expect(payload.subarray(compressedOffset + 12)).toEqual(new Uint8Array([0x90, ...raw]));
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('round-trips multi-block data byte-for-byte with best-match compression', () => {
|
|
127
|
+
const raw = Uint8Array.from({ length: 16_384 * 3 + 137 }, (_, index) => {
|
|
128
|
+
const column = index % 604;
|
|
129
|
+
const row = Math.floor(index / 604);
|
|
130
|
+
return (column * 31 + row * 17) & 0xff;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const packageData = buildPro2HostAssetPackage([{ name: 'wallpaper.bin', data: raw }]);
|
|
134
|
+
|
|
135
|
+
expect(decodeFirstPackageEntry(packageData, raw.byteLength)).toEqual(raw);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('falls back to 8 KiB blocks when a compressed 16 KiB block exceeds firmware capacity', () => {
|
|
139
|
+
let state = 0x12345678;
|
|
140
|
+
const raw = Uint8Array.from({ length: 16_384 }, () => {
|
|
141
|
+
state ^= state << 13;
|
|
142
|
+
state ^= state >>> 17;
|
|
143
|
+
state ^= state << 5;
|
|
144
|
+
return state & 0xff;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const packageData = buildPro2HostAssetPackage([{ name: 'wallpaper.bin', data: raw }]);
|
|
148
|
+
const archive = packageData.subarray(0x5f90);
|
|
149
|
+
const archiveView = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
|
|
150
|
+
const compressedOffset = archiveView.getUint32(42 + 0x100, true);
|
|
151
|
+
|
|
152
|
+
expect(archiveView.getUint16(compressedOffset + 2, true)).toBe(13);
|
|
153
|
+
expect(decodeFirstPackageEntry(packageData, raw.byteLength)).toEqual(raw);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test.each([
|
|
157
|
+
['1.0.0', false],
|
|
158
|
+
['1.0.1-beta.1', false],
|
|
159
|
+
['1.0.1', true],
|
|
160
|
+
['1.1.0', true],
|
|
161
|
+
[undefined, false],
|
|
162
|
+
])('selects package uploads for firmware %s', (firmwareVersion, expected) => {
|
|
163
|
+
expect(supportsPro2HostAssetPackage(firmwareVersion)).toBe(expected);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -66,8 +66,6 @@ const createRemoteConfig = (): RemoteConfigResponse =>
|
|
|
66
66
|
{
|
|
67
67
|
required: false,
|
|
68
68
|
version: [1, 0, 0],
|
|
69
|
-
url: 'https://example.com/pro2.okpkg',
|
|
70
|
-
fingerprint: 'a'.repeat(64),
|
|
71
69
|
changelog: { 'zh-CN': 'Pro2', 'en-US': 'Pro2' },
|
|
72
70
|
resources: { source: resourceSource },
|
|
73
71
|
},
|
|
@@ -79,8 +77,6 @@ const createRemoteConfig = (): RemoteConfigResponse =>
|
|
|
79
77
|
{
|
|
80
78
|
required: false,
|
|
81
79
|
version: [1, 0, 0],
|
|
82
|
-
url: 'https://example.com/neo.okpkg',
|
|
83
|
-
fingerprint: 'b'.repeat(64),
|
|
84
80
|
changelog: { 'zh-CN': 'Neo', 'en-US': 'Neo' },
|
|
85
81
|
resources: { source: neoResourceSource },
|
|
86
82
|
},
|
|
@@ -109,6 +105,11 @@ describe('Pro2 resource configuration', () => {
|
|
|
109
105
|
expect(
|
|
110
106
|
isProtocolV2ResourceArchiveEntryName('bundles/firmware_logo-pro2-prod_resource-signed.okpkg')
|
|
111
107
|
).toBe(true);
|
|
108
|
+
expect(
|
|
109
|
+
isProtocolV2ResourceArchiveEntryName(
|
|
110
|
+
'pro2-prod_resource/bundles/firmware_logo-pro2-prod_resource-signed.okpkg'
|
|
111
|
+
)
|
|
112
|
+
).toBe(true);
|
|
112
113
|
expect(
|
|
113
114
|
isProtocolV2ResourceArchiveEntryName(
|
|
114
115
|
'__MACOSX/pro2-prod_resource 2/bundles/._firmware_logo-pro2-prod_resource-signed.okpkg'
|
|
@@ -119,6 +120,8 @@ describe('Pro2 resource configuration', () => {
|
|
|
119
120
|
'bundles/._firmware_logo-pro2-prod_resource-signed.okpkg'
|
|
120
121
|
)
|
|
121
122
|
).toBe(false);
|
|
123
|
+
expect(isProtocolV2ResourceArchiveEntryName('pro2-prod_resource/manifest.json')).toBe(false);
|
|
124
|
+
expect(isProtocolV2ResourceArchiveEntryName('pro2-prod_resource/.DS_Store')).toBe(false);
|
|
122
125
|
});
|
|
123
126
|
|
|
124
127
|
test('reads the version, hashes, and direct device path from a RESC package', () => {
|
|
@@ -151,8 +151,42 @@ describe('Protocol V2 unlock semantics', () => {
|
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
expect(method.unlockPolicy).toBe('unlock-before-run');
|
|
154
|
-
expect(device.unlockDevice).
|
|
154
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, expect.any(Object));
|
|
155
155
|
expect(run).toHaveBeenCalledTimes(1);
|
|
156
|
+
expect(method.protocolV2UnlockContext).toMatchObject({
|
|
157
|
+
preflightCompleted: true,
|
|
158
|
+
preflightStatusRefreshed: true,
|
|
159
|
+
preflightMainPinSelected: true,
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('allows either PIN type when pre-unlocking hidden-wallet selection', async () => {
|
|
164
|
+
const method = new OpenWalletSession({
|
|
165
|
+
id: 1,
|
|
166
|
+
payload: { method: 'openWalletSession', mode: 'select-hidden' },
|
|
167
|
+
});
|
|
168
|
+
method.init();
|
|
169
|
+
method.run = jest.fn().mockResolvedValue({ walletType: 'hidden' }) as any;
|
|
170
|
+
const features = { unlocked: false };
|
|
171
|
+
const device = {
|
|
172
|
+
features,
|
|
173
|
+
commands: {
|
|
174
|
+
typedCall: jest.fn().mockResolvedValue({ message: { unlocked: false } }),
|
|
175
|
+
},
|
|
176
|
+
isProtocolV2: () => true,
|
|
177
|
+
isBootloader: () => false,
|
|
178
|
+
isRomloader: () => false,
|
|
179
|
+
updateProtocolV2Status: jest.fn(() => features),
|
|
180
|
+
unlockDevice: jest.fn().mockImplementation(() => {
|
|
181
|
+
features.unlocked = true;
|
|
182
|
+
return Promise.resolve(features);
|
|
183
|
+
}),
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
await runMethodWithUnlockPolicy(method, device as any);
|
|
187
|
+
|
|
188
|
+
expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Any, expect.any(Object));
|
|
189
|
+
expect(method.protocolV2UnlockContext?.preflightMainPinSelected).toBe(false);
|
|
156
190
|
});
|
|
157
191
|
|
|
158
192
|
test('pre-unlocks an unregistered wallet business method', async () => {
|