@sidhujag/sysweb3-keyring 1.0.544 → 1.0.547
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/coverage/clover.xml +2875 -0
- package/coverage/coverage-final.json +29468 -0
- package/coverage/lcov-report/base.css +354 -0
- package/coverage/lcov-report/block-navigation.js +85 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +320 -0
- package/coverage/lcov-report/prettify.css +101 -0
- package/coverage/lcov-report/prettify.js +1008 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +191 -0
- package/coverage/lcov-report/src/index.html +276 -0
- package/coverage/lcov-report/src/index.ts.html +114 -0
- package/coverage/lcov-report/src/initial-state.ts.html +558 -0
- package/coverage/lcov-report/src/keyring-manager.ts.html +6279 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/index.html +178 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/index.ts.html +144 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/appClient.ts.html +1560 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/bip32.ts.html +276 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/buffertools.ts.html +495 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/clientCommands.ts.html +1138 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/index.html +363 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts.html +289 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkle.ts.html +486 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkleMap.ts.html +240 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/policy.ts.html +342 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/psbtv2.ts.html +2388 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/varint.ts.html +453 -0
- package/coverage/lcov-report/src/ledger/consts.ts.html +177 -0
- package/coverage/lcov-report/src/ledger/index.html +216 -0
- package/coverage/lcov-report/src/ledger/index.ts.html +1371 -0
- package/coverage/lcov-report/src/ledger/utils.ts.html +102 -0
- package/coverage/lcov-report/src/signers.ts.html +591 -0
- package/coverage/lcov-report/src/storage.ts.html +198 -0
- package/coverage/lcov-report/src/transactions/ethereum.ts.html +5826 -0
- package/coverage/lcov-report/src/transactions/index.html +216 -0
- package/coverage/lcov-report/src/transactions/index.ts.html +93 -0
- package/coverage/lcov-report/src/transactions/syscoin.ts.html +1521 -0
- package/coverage/lcov-report/src/trezor/index.html +176 -0
- package/coverage/lcov-report/src/trezor/index.ts.html +2655 -0
- package/coverage/lcov-report/src/types.ts.html +1443 -0
- package/coverage/lcov-report/src/utils/derivation-paths.ts.html +486 -0
- package/coverage/lcov-report/src/utils/index.html +196 -0
- package/coverage/lcov-report/src/utils/psbt.ts.html +159 -0
- package/coverage/lcov-report/test/helpers/constants.ts.html +627 -0
- package/coverage/lcov-report/test/helpers/index.html +176 -0
- package/coverage/lcov.info +4832 -0
- package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/appClient.js +1 -124
- package/dist/cjs/ledger/bitcoin_client/lib/appClient.js.map +1 -0
- package/{cjs → dist/cjs}/transactions/ethereum.js +24 -11
- package/dist/cjs/transactions/ethereum.js.map +1 -0
- package/dist/package.json +50 -0
- package/{types → dist/types}/ledger/bitcoin_client/lib/appClient.d.ts +0 -6
- package/examples/basic-usage.js +140 -0
- package/jest.config.js +32 -0
- package/package.json +31 -13
- package/readme.md +201 -0
- package/src/declare.d.ts +7 -0
- package/src/errorUtils.ts +83 -0
- package/src/hardware-wallet-manager.ts +655 -0
- package/src/index.ts +12 -0
- package/src/initial-state.ts +108 -0
- package/src/keyring-manager.ts +2698 -0
- package/src/ledger/bitcoin_client/index.ts +19 -0
- package/src/ledger/bitcoin_client/lib/appClient.ts +405 -0
- package/src/ledger/bitcoin_client/lib/bip32.ts +61 -0
- package/src/ledger/bitcoin_client/lib/buffertools.ts +134 -0
- package/src/ledger/bitcoin_client/lib/clientCommands.ts +356 -0
- package/src/ledger/bitcoin_client/lib/constants.ts +12 -0
- package/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts +65 -0
- package/src/ledger/bitcoin_client/lib/merkle.ts +136 -0
- package/src/ledger/bitcoin_client/lib/merkleMap.ts +49 -0
- package/src/ledger/bitcoin_client/lib/policy.ts +91 -0
- package/src/ledger/bitcoin_client/lib/psbtv2.ts +768 -0
- package/src/ledger/bitcoin_client/lib/varint.ts +120 -0
- package/src/ledger/consts.ts +3 -0
- package/src/ledger/index.ts +685 -0
- package/src/ledger/types.ts +74 -0
- package/src/network-utils.ts +99 -0
- package/src/providers.ts +345 -0
- package/src/signers.ts +158 -0
- package/src/storage.ts +63 -0
- package/src/transactions/__tests__/integration.test.ts +303 -0
- package/src/transactions/__tests__/syscoin.test.ts +409 -0
- package/src/transactions/ethereum.ts +2503 -0
- package/src/transactions/index.ts +2 -0
- package/src/transactions/syscoin.ts +542 -0
- package/src/trezor/index.ts +1050 -0
- package/src/types.ts +366 -0
- package/src/utils/derivation-paths.ts +133 -0
- package/src/utils/psbt.ts +24 -0
- package/src/utils.ts +191 -0
- package/test/README.md +158 -0
- package/test/__mocks__/ledger-mock.js +20 -0
- package/test/__mocks__/trezor-mock.js +75 -0
- package/test/cleanup-summary.md +167 -0
- package/test/helpers/README.md +78 -0
- package/test/helpers/constants.ts +79 -0
- package/test/helpers/setup.ts +714 -0
- package/test/integration/import-validation.spec.ts +588 -0
- package/test/unit/hardware/ledger.spec.ts +869 -0
- package/test/unit/hardware/trezor.spec.ts +828 -0
- package/test/unit/keyring-manager/account-management.spec.ts +970 -0
- package/test/unit/keyring-manager/import-watchonly.spec.ts +181 -0
- package/test/unit/keyring-manager/import-wif.spec.ts +126 -0
- package/test/unit/keyring-manager/initialization.spec.ts +782 -0
- package/test/unit/keyring-manager/key-derivation.spec.ts +996 -0
- package/test/unit/keyring-manager/security.spec.ts +505 -0
- package/test/unit/keyring-manager/state-management.spec.ts +375 -0
- package/test/unit/network/network-management.spec.ts +372 -0
- package/test/unit/transactions/ethereum-transactions.spec.ts +382 -0
- package/test/unit/transactions/syscoin-transactions.spec.ts +615 -0
- package/tsconfig.json +14 -0
- package/cjs/ledger/bitcoin_client/lib/appClient.js.map +0 -1
- package/cjs/transactions/ethereum.js.map +0 -1
- /package/{README.md → dist/README.md} +0 -0
- /package/{cjs → dist/cjs}/errorUtils.js +0 -0
- /package/{cjs → dist/cjs}/errorUtils.js.map +0 -0
- /package/{cjs → dist/cjs}/hardware-wallet-manager.js +0 -0
- /package/{cjs → dist/cjs}/hardware-wallet-manager.js.map +0 -0
- /package/{cjs → dist/cjs}/index.js +0 -0
- /package/{cjs → dist/cjs}/index.js.map +0 -0
- /package/{cjs → dist/cjs}/initial-state.js +0 -0
- /package/{cjs → dist/cjs}/initial-state.js.map +0 -0
- /package/{cjs → dist/cjs}/keyring-manager.js +0 -0
- /package/{cjs → dist/cjs}/keyring-manager.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/consts.js +0 -0
- /package/{cjs → dist/cjs}/ledger/consts.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/index.js +0 -0
- /package/{cjs → dist/cjs}/ledger/index.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/types.js +0 -0
- /package/{cjs → dist/cjs}/ledger/types.js.map +0 -0
- /package/{cjs → dist/cjs}/network-utils.js +0 -0
- /package/{cjs → dist/cjs}/network-utils.js.map +0 -0
- /package/{cjs → dist/cjs}/providers.js +0 -0
- /package/{cjs → dist/cjs}/providers.js.map +0 -0
- /package/{cjs → dist/cjs}/signers.js +0 -0
- /package/{cjs → dist/cjs}/signers.js.map +0 -0
- /package/{cjs → dist/cjs}/storage.js +0 -0
- /package/{cjs → dist/cjs}/storage.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/index.js +0 -0
- /package/{cjs → dist/cjs}/transactions/index.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/syscoin.js +0 -0
- /package/{cjs → dist/cjs}/transactions/syscoin.js.map +0 -0
- /package/{cjs → dist/cjs}/trezor/index.js +0 -0
- /package/{cjs → dist/cjs}/trezor/index.js.map +0 -0
- /package/{cjs → dist/cjs}/types.js +0 -0
- /package/{cjs → dist/cjs}/types.js.map +0 -0
- /package/{cjs → dist/cjs}/utils/derivation-paths.js +0 -0
- /package/{cjs → dist/cjs}/utils/derivation-paths.js.map +0 -0
- /package/{cjs → dist/cjs}/utils/psbt.js +0 -0
- /package/{cjs → dist/cjs}/utils/psbt.js.map +0 -0
- /package/{cjs → dist/cjs}/utils.js +0 -0
- /package/{cjs → dist/cjs}/utils.js.map +0 -0
- /package/{types → dist/types}/errorUtils.d.ts +0 -0
- /package/{types → dist/types}/hardware-wallet-manager.d.ts +0 -0
- /package/{types → dist/types}/index.d.ts +0 -0
- /package/{types → dist/types}/initial-state.d.ts +0 -0
- /package/{types → dist/types}/keyring-manager.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/index.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/bip32.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/buffertools.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/clientCommands.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/constants.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/merkelizedPsbt.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/merkle.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/merkleMap.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/policy.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/psbtv2.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/varint.d.ts +0 -0
- /package/{types → dist/types}/ledger/consts.d.ts +0 -0
- /package/{types → dist/types}/ledger/index.d.ts +0 -0
- /package/{types → dist/types}/ledger/types.d.ts +0 -0
- /package/{types → dist/types}/network-utils.d.ts +0 -0
- /package/{types → dist/types}/providers.d.ts +0 -0
- /package/{types → dist/types}/signers.d.ts +0 -0
- /package/{types → dist/types}/storage.d.ts +0 -0
- /package/{types → dist/types}/transactions/__tests__/integration.test.d.ts +0 -0
- /package/{types → dist/types}/transactions/__tests__/syscoin.test.d.ts +0 -0
- /package/{types → dist/types}/transactions/ethereum.d.ts +0 -0
- /package/{types → dist/types}/transactions/index.d.ts +0 -0
- /package/{types → dist/types}/transactions/syscoin.d.ts +0 -0
- /package/{types → dist/types}/trezor/index.d.ts +0 -0
- /package/{types → dist/types}/types.d.ts +0 -0
- /package/{types → dist/types}/utils/derivation-paths.d.ts +0 -0
- /package/{types → dist/types}/utils/psbt.d.ts +0 -0
- /package/{types → dist/types}/utils.d.ts +0 -0
|
@@ -0,0 +1,828 @@
|
|
|
1
|
+
import { INetworkType } from '@sidhujag/sysweb3-network';
|
|
2
|
+
|
|
3
|
+
import { KeyringManager, KeyringAccountType } from '../../../src';
|
|
4
|
+
import { FAKE_PASSWORD, PEACE_SEED_PHRASE } from '../../helpers/constants';
|
|
5
|
+
import { setupMocks } from '../../helpers/setup';
|
|
6
|
+
|
|
7
|
+
describe('Trezor Hardware Wallet', () => {
|
|
8
|
+
let keyringManager: KeyringManager;
|
|
9
|
+
let mockVaultStateGetter: jest.Mock;
|
|
10
|
+
let currentVaultState: any;
|
|
11
|
+
let accountCounter = 0;
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
setupMocks();
|
|
15
|
+
// Set up vault-keys that would normally be created by Pali's MainController
|
|
16
|
+
await setupTestVault(FAKE_PASSWORD);
|
|
17
|
+
|
|
18
|
+
// Reset counter for each test
|
|
19
|
+
accountCounter = 0;
|
|
20
|
+
|
|
21
|
+
// Set up UTXO vault state
|
|
22
|
+
currentVaultState = createMockVaultState({
|
|
23
|
+
activeAccountId: 0,
|
|
24
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
25
|
+
networkType: INetworkType.Syscoin,
|
|
26
|
+
chainId: 57,
|
|
27
|
+
});
|
|
28
|
+
mockVaultStateGetter = jest.fn(() => currentVaultState);
|
|
29
|
+
|
|
30
|
+
keyringManager = await KeyringManager.createInitialized(
|
|
31
|
+
PEACE_SEED_PHRASE,
|
|
32
|
+
FAKE_PASSWORD,
|
|
33
|
+
mockVaultStateGetter
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Mock Trezor signer methods with dynamic responses
|
|
37
|
+
keyringManager.trezorSigner.getAccountInfo = jest
|
|
38
|
+
.fn()
|
|
39
|
+
.mockImplementation(() => {
|
|
40
|
+
accountCounter++;
|
|
41
|
+
return Promise.resolve({
|
|
42
|
+
descriptor: `xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKpXqNPQ8R5ziHackBvPaKjYNDvyVp7ytpqqxV5BDK1Co76d4oVGkYvJXFFpbhctBCTKRQ8Y7HNrxE${accountCounter}`,
|
|
43
|
+
balance: '100000000', // 1 SYS in satoshis
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
keyringManager.trezorSigner.getPublicKey = jest.fn().mockResolvedValue({
|
|
48
|
+
publicKey: '0x04mock_public_key',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Mock getAddress for Trezor accounts with dynamic addresses
|
|
52
|
+
keyringManager.getAddress = jest.fn().mockImplementation(() => {
|
|
53
|
+
return Promise.resolve(`sys1qmock_trezor_address_${accountCounter}`);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterEach(async () => {
|
|
58
|
+
// Clean up hardware wallet connections
|
|
59
|
+
if (keyringManager) {
|
|
60
|
+
await keyringManager.destroy();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('Account Import', () => {
|
|
65
|
+
it('should import Trezor account', async () => {
|
|
66
|
+
const account = await keyringManager.importTrezorAccount('My Trezor');
|
|
67
|
+
|
|
68
|
+
expect(account).toBeDefined();
|
|
69
|
+
expect(account.id).toBe(0); // First Trezor account gets ID 0
|
|
70
|
+
expect(account.label).toBe('My Trezor');
|
|
71
|
+
expect(account.isTrezorWallet).toBe(true);
|
|
72
|
+
expect(account.isLedgerWallet).toBe(false);
|
|
73
|
+
expect(account.isImported).toBe(false);
|
|
74
|
+
expect(account.xprv).toBe(''); // Hardware wallets store empty xprv
|
|
75
|
+
|
|
76
|
+
// Update vault state with imported account
|
|
77
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][account.id] = {
|
|
78
|
+
id: account.id,
|
|
79
|
+
label: account.label,
|
|
80
|
+
address: account.address,
|
|
81
|
+
xpub: account.xpub,
|
|
82
|
+
xprv: '',
|
|
83
|
+
isImported: false,
|
|
84
|
+
isTrezorWallet: true,
|
|
85
|
+
isLedgerWallet: false,
|
|
86
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
87
|
+
assets: { syscoin: [], ethereum: [] },
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should import multiple Trezor accounts', async () => {
|
|
92
|
+
const account1 = await keyringManager.importTrezorAccount();
|
|
93
|
+
|
|
94
|
+
// Update vault state
|
|
95
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][account1.id] = {
|
|
96
|
+
id: account1.id,
|
|
97
|
+
label: account1.label,
|
|
98
|
+
address: account1.address,
|
|
99
|
+
xpub: account1.xpub,
|
|
100
|
+
xprv: '',
|
|
101
|
+
isImported: false,
|
|
102
|
+
isTrezorWallet: true,
|
|
103
|
+
isLedgerWallet: false,
|
|
104
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
105
|
+
assets: { syscoin: [], ethereum: [] },
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const account2 = await keyringManager.importTrezorAccount();
|
|
109
|
+
|
|
110
|
+
// Update vault state
|
|
111
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][account2.id] = {
|
|
112
|
+
id: account2.id,
|
|
113
|
+
label: account2.label,
|
|
114
|
+
address: account2.address,
|
|
115
|
+
xpub: account2.xpub,
|
|
116
|
+
xprv: '',
|
|
117
|
+
isImported: false,
|
|
118
|
+
isTrezorWallet: true,
|
|
119
|
+
isLedgerWallet: false,
|
|
120
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
121
|
+
assets: { syscoin: [], ethereum: [] },
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const account3 = await keyringManager.importTrezorAccount(
|
|
125
|
+
'Custom Trezor'
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Update vault state
|
|
129
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][account3.id] = {
|
|
130
|
+
id: account3.id,
|
|
131
|
+
label: account3.label,
|
|
132
|
+
address: account3.address,
|
|
133
|
+
xpub: account3.xpub,
|
|
134
|
+
xprv: '',
|
|
135
|
+
isImported: false,
|
|
136
|
+
isTrezorWallet: true,
|
|
137
|
+
isLedgerWallet: false,
|
|
138
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
139
|
+
assets: { syscoin: [], ethereum: [] },
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
expect(account1.id).toBe(0);
|
|
143
|
+
expect(account1.label).toBe('Trezor 1');
|
|
144
|
+
|
|
145
|
+
expect(account2.id).toBe(1);
|
|
146
|
+
expect(account2.label).toBe('Trezor 2');
|
|
147
|
+
|
|
148
|
+
expect(account3.id).toBe(2);
|
|
149
|
+
expect(account3.label).toBe('Custom Trezor');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should handle Trezor import for EVM networks', async () => {
|
|
153
|
+
// Set up EVM vault state
|
|
154
|
+
const evmVaultState = createMockVaultState({
|
|
155
|
+
activeAccountId: 0,
|
|
156
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
157
|
+
networkType: INetworkType.Ethereum,
|
|
158
|
+
chainId: 1,
|
|
159
|
+
});
|
|
160
|
+
const evmVaultStateGetter = jest.fn(() => evmVaultState);
|
|
161
|
+
|
|
162
|
+
// Create EVM keyring
|
|
163
|
+
const evmKeyring = await KeyringManager.createInitialized(
|
|
164
|
+
PEACE_SEED_PHRASE,
|
|
165
|
+
FAKE_PASSWORD,
|
|
166
|
+
evmVaultStateGetter
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// Mock Trezor EVM methods with proper EVM address
|
|
170
|
+
evmKeyring.trezorSigner.getPublicKey = jest.fn().mockResolvedValue({
|
|
171
|
+
publicKey: '0x04mock_evm_public_key',
|
|
172
|
+
});
|
|
173
|
+
evmKeyring.trezorSigner.getAccountInfo = jest.fn().mockResolvedValue({
|
|
174
|
+
descriptor: '0x742D35Cc6634C0532925a3b844bc9e7595f2bd9f', // Return EVM address directly as descriptor
|
|
175
|
+
balance: '1000000000000000000', // 1 ETH in wei
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const account = await evmKeyring.importTrezorAccount('Trezor ETH');
|
|
179
|
+
|
|
180
|
+
expect(account).toBeDefined();
|
|
181
|
+
expect(account.address.startsWith('0x')).toBe(true);
|
|
182
|
+
expect(account.isTrezorWallet).toBe(true);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should reject duplicate Trezor addresses', async () => {
|
|
186
|
+
// First import
|
|
187
|
+
const firstAccount = await keyringManager.importTrezorAccount();
|
|
188
|
+
|
|
189
|
+
// Update vault state with first account
|
|
190
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][firstAccount.id] = {
|
|
191
|
+
id: firstAccount.id,
|
|
192
|
+
label: firstAccount.label,
|
|
193
|
+
address: firstAccount.address,
|
|
194
|
+
xpub: firstAccount.xpub,
|
|
195
|
+
xprv: '',
|
|
196
|
+
isImported: false,
|
|
197
|
+
isTrezorWallet: true,
|
|
198
|
+
isLedgerWallet: false,
|
|
199
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
200
|
+
assets: { syscoin: [], ethereum: [] },
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// Mock Trezor returning same address
|
|
204
|
+
keyringManager.trezorSigner.getAccountInfo = jest.fn().mockResolvedValue({
|
|
205
|
+
descriptor: 'xpub_duplicate',
|
|
206
|
+
balance: '100000000',
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Should reject duplicate
|
|
210
|
+
await expect(keyringManager.importTrezorAccount()).rejects.toThrow(
|
|
211
|
+
'Account already exists'
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe('Account Management', () => {
|
|
217
|
+
beforeEach(async () => {
|
|
218
|
+
// Import a Trezor account
|
|
219
|
+
const account = await keyringManager.importTrezorAccount('Test Trezor');
|
|
220
|
+
|
|
221
|
+
// Update vault state with imported account
|
|
222
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][account.id] = {
|
|
223
|
+
id: account.id,
|
|
224
|
+
label: account.label,
|
|
225
|
+
address: account.address,
|
|
226
|
+
xpub: account.xpub,
|
|
227
|
+
xprv: '',
|
|
228
|
+
isImported: false,
|
|
229
|
+
isTrezorWallet: true,
|
|
230
|
+
isLedgerWallet: false,
|
|
231
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
232
|
+
assets: { syscoin: [], ethereum: [] },
|
|
233
|
+
};
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should switch to Trezor account', async () => {
|
|
237
|
+
// Update vault state to set Trezor as active
|
|
238
|
+
currentVaultState.activeAccount = {
|
|
239
|
+
id: 0,
|
|
240
|
+
type: KeyringAccountType.Trezor,
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// Account switch is handled by vault state update
|
|
244
|
+
|
|
245
|
+
const { activeAccount, activeAccountType } =
|
|
246
|
+
keyringManager.getActiveAccount();
|
|
247
|
+
expect(activeAccountType).toBe(KeyringAccountType.Trezor);
|
|
248
|
+
expect(activeAccount.isTrezorWallet).toBe(true);
|
|
249
|
+
expect(activeAccount.label).toBe('Test Trezor');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should get Trezor account xpub', async () => {
|
|
253
|
+
// Update vault state to set Trezor as active
|
|
254
|
+
currentVaultState.activeAccount = {
|
|
255
|
+
id: 0,
|
|
256
|
+
type: KeyringAccountType.Trezor,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Account switch is handled by vault state update
|
|
260
|
+
|
|
261
|
+
const xpub = keyringManager.getAccountXpub();
|
|
262
|
+
expect(xpub).toBeDefined();
|
|
263
|
+
expect(typeof xpub).toBe('string');
|
|
264
|
+
// For UTXO it should be an xpub, for EVM it's the public key
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should handle mixed account types', async () => {
|
|
268
|
+
// Add HD account
|
|
269
|
+
const hdAccount = await keyringManager.addNewAccount('HD Account 2');
|
|
270
|
+
|
|
271
|
+
// Update vault state with HD account
|
|
272
|
+
currentVaultState.accounts[KeyringAccountType.HDAccount][hdAccount.id] = {
|
|
273
|
+
id: hdAccount.id,
|
|
274
|
+
label: hdAccount.label,
|
|
275
|
+
address: hdAccount.address,
|
|
276
|
+
xpub: hdAccount.xpub,
|
|
277
|
+
xprv: '',
|
|
278
|
+
isImported: false,
|
|
279
|
+
isTrezorWallet: false,
|
|
280
|
+
isLedgerWallet: false,
|
|
281
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
282
|
+
assets: { syscoin: [], ethereum: [] },
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// Import another Trezor
|
|
286
|
+
const trezor2Account = await keyringManager.importTrezorAccount(
|
|
287
|
+
'Trezor 2'
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// Update vault state with second Trezor
|
|
291
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][trezor2Account.id] =
|
|
292
|
+
{
|
|
293
|
+
id: trezor2Account.id,
|
|
294
|
+
label: trezor2Account.label,
|
|
295
|
+
address: trezor2Account.address,
|
|
296
|
+
xpub: trezor2Account.xpub,
|
|
297
|
+
xprv: '',
|
|
298
|
+
isImported: false,
|
|
299
|
+
isTrezorWallet: true,
|
|
300
|
+
isLedgerWallet: false,
|
|
301
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
302
|
+
assets: { syscoin: [], ethereum: [] },
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// Switch between them
|
|
306
|
+
currentVaultState.activeAccount = {
|
|
307
|
+
id: 0,
|
|
308
|
+
type: KeyringAccountType.HDAccount,
|
|
309
|
+
};
|
|
310
|
+
// Account switch is handled by vault state update
|
|
311
|
+
expect(keyringManager.getActiveAccount().activeAccountType).toBe(
|
|
312
|
+
KeyringAccountType.HDAccount
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
currentVaultState.activeAccount = {
|
|
316
|
+
id: 0,
|
|
317
|
+
type: KeyringAccountType.Trezor,
|
|
318
|
+
};
|
|
319
|
+
// Account switch is handled by vault state update
|
|
320
|
+
expect(keyringManager.getActiveAccount().activeAccountType).toBe(
|
|
321
|
+
KeyringAccountType.Trezor
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
currentVaultState.activeAccount = {
|
|
325
|
+
id: trezor2Account.id,
|
|
326
|
+
type: KeyringAccountType.Trezor,
|
|
327
|
+
};
|
|
328
|
+
// Account switch is handled by vault state update
|
|
329
|
+
expect(keyringManager.getActiveAccount().activeAccount.label).toBe(
|
|
330
|
+
'Trezor 2'
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe('Transaction Signing', () => {
|
|
336
|
+
beforeEach(async () => {
|
|
337
|
+
// Mock syscoinjs-lib main signer
|
|
338
|
+
const mockSigner = {
|
|
339
|
+
hd: jest.fn(),
|
|
340
|
+
main: {
|
|
341
|
+
send: jest.fn().mockResolvedValue({
|
|
342
|
+
extractTransaction: () => ({
|
|
343
|
+
getId: () => 'mock_transaction_id',
|
|
344
|
+
}),
|
|
345
|
+
}),
|
|
346
|
+
blockbookURL: 'https://blockbook.test',
|
|
347
|
+
createTransaction: jest.fn().mockResolvedValue({
|
|
348
|
+
psbt: 'mock_psbt',
|
|
349
|
+
fee: 1000,
|
|
350
|
+
}),
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// Mock the syscoinTransaction getSigner method directly
|
|
355
|
+
(keyringManager.syscoinTransaction as any).getSigner = jest
|
|
356
|
+
.fn()
|
|
357
|
+
.mockReturnValue(mockSigner);
|
|
358
|
+
|
|
359
|
+
const account = await keyringManager.importTrezorAccount('Signing Test');
|
|
360
|
+
if (account) {
|
|
361
|
+
// Update vault state to set Trezor as active
|
|
362
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][account.id] = {
|
|
363
|
+
id: account.id,
|
|
364
|
+
label: account.label,
|
|
365
|
+
address: account.address,
|
|
366
|
+
xpub: account.xpub,
|
|
367
|
+
xprv: '',
|
|
368
|
+
isImported: false,
|
|
369
|
+
isTrezorWallet: true,
|
|
370
|
+
isLedgerWallet: false,
|
|
371
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
372
|
+
assets: { syscoin: [], ethereum: [] },
|
|
373
|
+
};
|
|
374
|
+
currentVaultState.activeAccount = {
|
|
375
|
+
id: account.id,
|
|
376
|
+
type: KeyringAccountType.Trezor,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('should prepare PSBT for Trezor signing', async () => {
|
|
382
|
+
const psbtData = {
|
|
383
|
+
psbt: 'valid_psbt_data',
|
|
384
|
+
assets: [],
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// Mock the entire signPSBT method to avoid PSBT parsing
|
|
388
|
+
const originalSignPSBT = keyringManager.syscoinTransaction.signPSBT;
|
|
389
|
+
keyringManager.syscoinTransaction.signPSBT = jest.fn().mockResolvedValue({
|
|
390
|
+
psbt: 'mock_signed_psbt_data',
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
const result = await keyringManager.syscoinTransaction.signPSBT({
|
|
394
|
+
psbt: psbtData,
|
|
395
|
+
isTrezor: true,
|
|
396
|
+
isLedger: false,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
expect(result).toBeDefined();
|
|
400
|
+
expect(result.psbt).toBe('mock_signed_psbt_data');
|
|
401
|
+
expect(keyringManager.syscoinTransaction.signPSBT).toHaveBeenCalledWith(
|
|
402
|
+
expect.objectContaining({
|
|
403
|
+
isTrezor: true,
|
|
404
|
+
isLedger: false,
|
|
405
|
+
})
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
// Restore original method
|
|
409
|
+
keyringManager.syscoinTransaction.signPSBT = originalSignPSBT;
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('should handle Trezor transaction flow', async () => {
|
|
413
|
+
const xpub = keyringManager.getAccountXpub();
|
|
414
|
+
expect(xpub).toBeDefined();
|
|
415
|
+
|
|
416
|
+
// Mock the PSBT parsing for sendTransaction too
|
|
417
|
+
const mockSyscoinjs = require('syscoinjs-lib');
|
|
418
|
+
const originalImportPsbtFromJson = mockSyscoinjs.utils.importPsbtFromJson;
|
|
419
|
+
mockSyscoinjs.utils.importPsbtFromJson = jest.fn().mockReturnValue({
|
|
420
|
+
extractTransaction: jest.fn().mockReturnValue({
|
|
421
|
+
getId: jest.fn().mockReturnValue('mock_transaction_id'),
|
|
422
|
+
}),
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// Mock transaction creation - use current sendTransaction API signature
|
|
426
|
+
const signedPsbt = {
|
|
427
|
+
psbt: 'valid_psbt_data',
|
|
428
|
+
assets: [],
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const result = await keyringManager.syscoinTransaction.sendTransaction(
|
|
432
|
+
signedPsbt
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
expect(result.txid).toBeDefined();
|
|
436
|
+
|
|
437
|
+
// Restore original function
|
|
438
|
+
mockSyscoinjs.utils.importPsbtFromJson = originalImportPsbtFromJson;
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('should handle EVM transaction signing for Trezor', async () => {
|
|
442
|
+
// Set up EVM vault state
|
|
443
|
+
const evmVaultState = createMockVaultState({
|
|
444
|
+
activeAccountId: 0,
|
|
445
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
446
|
+
networkType: INetworkType.Ethereum,
|
|
447
|
+
chainId: 1,
|
|
448
|
+
});
|
|
449
|
+
const evmVaultStateGetter = jest.fn(() => evmVaultState);
|
|
450
|
+
|
|
451
|
+
// Create EVM keyring with Trezor
|
|
452
|
+
const evmKeyring = await KeyringManager.createInitialized(
|
|
453
|
+
PEACE_SEED_PHRASE,
|
|
454
|
+
FAKE_PASSWORD,
|
|
455
|
+
evmVaultStateGetter
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
// Mock Trezor EVM methods
|
|
459
|
+
evmKeyring.trezorSigner.getAccountInfo = jest.fn().mockResolvedValue({
|
|
460
|
+
descriptor: '0x742D35Cc6634C0532925a3b844bc9e7595f2bd9f',
|
|
461
|
+
balance: '1000000000000000000',
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
evmKeyring.trezorSigner.getPublicKey = jest.fn().mockResolvedValue({
|
|
465
|
+
publicKey: '0x04mock_evm_public_key',
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
evmKeyring.trezorSigner.signEthTransaction = jest.fn().mockResolvedValue({
|
|
469
|
+
v: '0x1c',
|
|
470
|
+
r: '0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef12345678',
|
|
471
|
+
s: '0x987654321fedcba987654321fedcba987654321fedcba987654321fedcba987654',
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// Import Trezor account
|
|
475
|
+
const evmAccount = await evmKeyring.importTrezorAccount('Trezor ETH');
|
|
476
|
+
if (evmAccount) {
|
|
477
|
+
// Update vault state to set Trezor as active
|
|
478
|
+
evmVaultState.accounts[KeyringAccountType.Trezor][evmAccount.id] = {
|
|
479
|
+
id: evmAccount.id,
|
|
480
|
+
label: evmAccount.label,
|
|
481
|
+
address: evmAccount.address,
|
|
482
|
+
xpub: evmAccount.xpub,
|
|
483
|
+
xprv: '',
|
|
484
|
+
isImported: false,
|
|
485
|
+
isTrezorWallet: true,
|
|
486
|
+
isLedgerWallet: false,
|
|
487
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
488
|
+
assets: { syscoin: [], ethereum: [] },
|
|
489
|
+
};
|
|
490
|
+
evmVaultState.activeAccount = {
|
|
491
|
+
id: evmAccount.id,
|
|
492
|
+
type: KeyringAccountType.Trezor,
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// Account switch is handled by vault state update
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Test EVM transaction signing
|
|
499
|
+
expect(evmKeyring.trezorSigner.signEthTransaction).toBeDefined();
|
|
500
|
+
expect(evmAccount.address.startsWith('0x')).toBe(true);
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
describe('Network Support', () => {
|
|
505
|
+
it('should support Trezor on different UTXO networks', async () => {
|
|
506
|
+
// Set up testnet vault state
|
|
507
|
+
const testnetVaultState = createMockVaultState({
|
|
508
|
+
activeAccountId: 0,
|
|
509
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
510
|
+
networkType: INetworkType.Syscoin,
|
|
511
|
+
chainId: 5700,
|
|
512
|
+
});
|
|
513
|
+
const testnetVaultStateGetter = jest.fn(() => testnetVaultState);
|
|
514
|
+
|
|
515
|
+
// Test on testnet
|
|
516
|
+
const testnetKeyring = await KeyringManager.createInitialized(
|
|
517
|
+
PEACE_SEED_PHRASE,
|
|
518
|
+
FAKE_PASSWORD,
|
|
519
|
+
testnetVaultStateGetter
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
const account = await testnetKeyring.importTrezorAccount(
|
|
523
|
+
'Testnet Trezor'
|
|
524
|
+
);
|
|
525
|
+
expect(account.address.match(/^(sys1|tsys1)/)).toBeTruthy();
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it('should maintain separate Trezor accounts per network', async () => {
|
|
529
|
+
// Import on mainnet
|
|
530
|
+
const mainnetAccount = await keyringManager.importTrezorAccount(
|
|
531
|
+
'Mainnet Trezor'
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
// Set up testnet vault state
|
|
535
|
+
const testnetVaultState = createMockVaultState({
|
|
536
|
+
activeAccountId: 0,
|
|
537
|
+
activeAccountType: KeyringAccountType.HDAccount,
|
|
538
|
+
networkType: INetworkType.Syscoin,
|
|
539
|
+
chainId: 5700,
|
|
540
|
+
});
|
|
541
|
+
const testnetVaultStateGetter = jest.fn(() => testnetVaultState);
|
|
542
|
+
|
|
543
|
+
// Create testnet keyring
|
|
544
|
+
const testnetKeyring = await KeyringManager.createInitialized(
|
|
545
|
+
PEACE_SEED_PHRASE,
|
|
546
|
+
FAKE_PASSWORD,
|
|
547
|
+
testnetVaultStateGetter
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
// Import on testnet
|
|
551
|
+
const testnetAccount = await testnetKeyring.importTrezorAccount(
|
|
552
|
+
'Testnet Trezor'
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
// Accounts should be independent
|
|
556
|
+
expect(mainnetAccount.address).not.toBe(testnetAccount.address);
|
|
557
|
+
expect(mainnetAccount.address.match(/^(sys1|tsys1)/)).toBeTruthy();
|
|
558
|
+
expect(testnetAccount.address.match(/^(sys1|tsys1)/)).toBeTruthy();
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
describe('Error Handling', () => {
|
|
563
|
+
it('should handle Trezor connection errors', async () => {
|
|
564
|
+
// Mock connection failure
|
|
565
|
+
keyringManager.trezorSigner.getAccountInfo = jest
|
|
566
|
+
.fn()
|
|
567
|
+
.mockRejectedValue(new Error('Trezor not connected'));
|
|
568
|
+
|
|
569
|
+
await expect(keyringManager.importTrezorAccount()).rejects.toThrow(
|
|
570
|
+
'Trezor not connected'
|
|
571
|
+
);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('should handle invalid Trezor responses', async () => {
|
|
575
|
+
// Mock invalid response
|
|
576
|
+
keyringManager.trezorSigner.getAccountInfo = jest.fn().mockResolvedValue({
|
|
577
|
+
descriptor: null,
|
|
578
|
+
balance: null,
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
await expect(keyringManager.importTrezorAccount()).rejects.toThrow(
|
|
582
|
+
'Something wrong happened'
|
|
583
|
+
);
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it('should handle Trezor device disconnection during signing', async () => {
|
|
587
|
+
const account = await keyringManager.importTrezorAccount(
|
|
588
|
+
'Disconnect Test'
|
|
589
|
+
);
|
|
590
|
+
if (account) {
|
|
591
|
+
// Update vault state to set Trezor as active
|
|
592
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][account.id] = {
|
|
593
|
+
id: account.id,
|
|
594
|
+
label: account.label,
|
|
595
|
+
address: account.address,
|
|
596
|
+
xpub: account.xpub,
|
|
597
|
+
xprv: '',
|
|
598
|
+
isImported: false,
|
|
599
|
+
isTrezorWallet: true,
|
|
600
|
+
isLedgerWallet: false,
|
|
601
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
602
|
+
assets: { syscoin: [], ethereum: [] },
|
|
603
|
+
};
|
|
604
|
+
currentVaultState.activeAccount = {
|
|
605
|
+
id: account.id,
|
|
606
|
+
type: KeyringAccountType.Trezor,
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// Account switch is handled by vault state update
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Mock Trezor disconnection during signing
|
|
613
|
+
keyringManager.trezorSigner.signUtxoTransaction = jest
|
|
614
|
+
.fn()
|
|
615
|
+
.mockRejectedValue(new Error('Device disconnected'));
|
|
616
|
+
|
|
617
|
+
const psbtData = {
|
|
618
|
+
psbt: 'valid_psbt_data',
|
|
619
|
+
assets: [],
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
await expect(
|
|
623
|
+
keyringManager.syscoinTransaction.signPSBT({
|
|
624
|
+
psbt: psbtData,
|
|
625
|
+
isTrezor: true,
|
|
626
|
+
isLedger: false,
|
|
627
|
+
})
|
|
628
|
+
).rejects.toThrow();
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
it('should handle Trezor firmware update required', async () => {
|
|
632
|
+
// Mock firmware update required error
|
|
633
|
+
keyringManager.trezorSigner.getAccountInfo = jest
|
|
634
|
+
.fn()
|
|
635
|
+
.mockRejectedValue(new Error('Device firmware update required'));
|
|
636
|
+
|
|
637
|
+
await expect(keyringManager.importTrezorAccount()).rejects.toThrow(
|
|
638
|
+
'Device firmware update required'
|
|
639
|
+
);
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
describe('Security', () => {
|
|
644
|
+
it('should not expose private keys for hardware wallets', async () => {
|
|
645
|
+
const account = await keyringManager.importTrezorAccount('Security Test');
|
|
646
|
+
|
|
647
|
+
expect(account).toBeDefined();
|
|
648
|
+
expect(account.xprv).toBe(''); // Should be empty for hardware wallets
|
|
649
|
+
expect(account.isTrezorWallet).toBe(true);
|
|
650
|
+
|
|
651
|
+
// Should not be able to get private key for hardware wallet
|
|
652
|
+
await expect(
|
|
653
|
+
keyringManager.getPrivateKeyByAccountId(
|
|
654
|
+
account.id,
|
|
655
|
+
KeyringAccountType.Trezor,
|
|
656
|
+
FAKE_PASSWORD
|
|
657
|
+
)
|
|
658
|
+
).rejects.toThrow();
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it('should maintain hardware wallet isolation', async () => {
|
|
662
|
+
const account = await keyringManager.importTrezorAccount(
|
|
663
|
+
'Isolation Test'
|
|
664
|
+
);
|
|
665
|
+
|
|
666
|
+
if (account) {
|
|
667
|
+
// Update vault state
|
|
668
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][account.id] = {
|
|
669
|
+
id: account.id,
|
|
670
|
+
label: account.label,
|
|
671
|
+
address: account.address,
|
|
672
|
+
xpub: account.xpub,
|
|
673
|
+
xprv: '',
|
|
674
|
+
isImported: false,
|
|
675
|
+
isTrezorWallet: true,
|
|
676
|
+
isLedgerWallet: false,
|
|
677
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
678
|
+
assets: { syscoin: [], ethereum: [] },
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
// Verify Trezor accounts are completely separate from HD accounts
|
|
682
|
+
const hdAccounts =
|
|
683
|
+
currentVaultState.accounts[KeyringAccountType.HDAccount];
|
|
684
|
+
const trezorAccounts =
|
|
685
|
+
currentVaultState.accounts[KeyringAccountType.Trezor];
|
|
686
|
+
|
|
687
|
+
expect(Object.keys(hdAccounts)).toHaveLength(1); // Initial HD account
|
|
688
|
+
expect(Object.keys(trezorAccounts)).toHaveLength(1); // One imported Trezor account
|
|
689
|
+
|
|
690
|
+
// Verify no cross-contamination of account types
|
|
691
|
+
Object.values(hdAccounts).forEach((account: any) => {
|
|
692
|
+
expect(account.isTrezorWallet).toBe(false);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
Object.values(trezorAccounts).forEach((account: any) => {
|
|
696
|
+
expect(account.isTrezorWallet).toBe(true);
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
it('should handle hardware wallet re-initialization securely', async () => {
|
|
702
|
+
const account = await keyringManager.importTrezorAccount('Reinit Test');
|
|
703
|
+
|
|
704
|
+
if (account) {
|
|
705
|
+
// Update vault state
|
|
706
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][account.id] = {
|
|
707
|
+
id: account.id,
|
|
708
|
+
label: account.label,
|
|
709
|
+
address: account.address,
|
|
710
|
+
xpub: account.xpub,
|
|
711
|
+
xprv: '',
|
|
712
|
+
isImported: false,
|
|
713
|
+
isTrezorWallet: true,
|
|
714
|
+
isLedgerWallet: false,
|
|
715
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
716
|
+
assets: { syscoin: [], ethereum: [] },
|
|
717
|
+
};
|
|
718
|
+
currentVaultState.activeAccount = {
|
|
719
|
+
id: account.id,
|
|
720
|
+
type: KeyringAccountType.Trezor,
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
// Account switch is handled by vault state update
|
|
724
|
+
|
|
725
|
+
// Lock and unlock to test re-initialization
|
|
726
|
+
keyringManager.lockWallet();
|
|
727
|
+
|
|
728
|
+
// Mock hardware wallet communication error during unlock
|
|
729
|
+
keyringManager.trezorSigner.getAccountInfo = jest
|
|
730
|
+
.fn()
|
|
731
|
+
.mockRejectedValue(new Error('Trezor device not found'));
|
|
732
|
+
|
|
733
|
+
// Should still unlock successfully - account info comes from vault
|
|
734
|
+
const unlockResult = await keyringManager.unlock(FAKE_PASSWORD);
|
|
735
|
+
expect(unlockResult.canLogin).toBe(true);
|
|
736
|
+
|
|
737
|
+
// Account info should still be accessible from vault
|
|
738
|
+
const activeAfter = keyringManager.getActiveAccount();
|
|
739
|
+
expect(activeAfter.activeAccount.label).toBe('Reinit Test');
|
|
740
|
+
expect(activeAfter.activeAccount.isTrezorWallet).toBe(true);
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
it('should verify UTXO address on Trezor device', async () => {
|
|
745
|
+
// Mock trezorSigner.verifyAddress to simulate device verification
|
|
746
|
+
keyringManager.trezorSigner.verifyUtxoAddress = jest
|
|
747
|
+
.fn()
|
|
748
|
+
.mockResolvedValue('sys1qmock_trezor_verified_address');
|
|
749
|
+
|
|
750
|
+
const account = await keyringManager.importTrezorAccount('Verify Test');
|
|
751
|
+
|
|
752
|
+
if (account) {
|
|
753
|
+
// Update vault state
|
|
754
|
+
currentVaultState.accounts[KeyringAccountType.Trezor][account.id] = {
|
|
755
|
+
id: account.id,
|
|
756
|
+
label: account.label,
|
|
757
|
+
address: account.address,
|
|
758
|
+
xpub: account.xpub,
|
|
759
|
+
xprv: '',
|
|
760
|
+
isImported: false,
|
|
761
|
+
isTrezorWallet: true,
|
|
762
|
+
isLedgerWallet: false,
|
|
763
|
+
balances: { syscoin: 0, ethereum: 0 },
|
|
764
|
+
assets: { syscoin: [], ethereum: [] },
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
// Test verifyUtxoAddress
|
|
768
|
+
const verifiedAddress =
|
|
769
|
+
await keyringManager.trezorSigner.verifyUtxoAddress(
|
|
770
|
+
account.id,
|
|
771
|
+
'sys',
|
|
772
|
+
57
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
expect(verifiedAddress).toBe('sys1qmock_trezor_verified_address');
|
|
776
|
+
|
|
777
|
+
// Verify that the mock was called
|
|
778
|
+
expect(
|
|
779
|
+
keyringManager.trezorSigner.verifyUtxoAddress
|
|
780
|
+
).toHaveBeenCalledWith(account.id, 'sys', 57);
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
it('should handle user cancellation during address verification', async () => {
|
|
785
|
+
// Mock getAccountInfo to return quickly
|
|
786
|
+
const originalGetAccountInfo = keyringManager.trezorSigner.getAccountInfo;
|
|
787
|
+
keyringManager.trezorSigner.getAccountInfo = jest.fn().mockResolvedValue({
|
|
788
|
+
descriptor: 'test-xpub',
|
|
789
|
+
balance: '0',
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
// Create an account successfully first
|
|
793
|
+
let account;
|
|
794
|
+
try {
|
|
795
|
+
account = await keyringManager.importTrezorAccount('Cancel Test');
|
|
796
|
+
expect(account).toBeDefined();
|
|
797
|
+
} finally {
|
|
798
|
+
// Restore getAccountInfo
|
|
799
|
+
keyringManager.trezorSigner.getAccountInfo = originalGetAccountInfo;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Mock verifyUtxoAddress directly to avoid retry delays
|
|
803
|
+
keyringManager.trezorSigner.verifyUtxoAddress = jest
|
|
804
|
+
.fn()
|
|
805
|
+
.mockRejectedValue(new Error('Address verification cancelled by user'));
|
|
806
|
+
|
|
807
|
+
try {
|
|
808
|
+
// Attempt to verify address - this should throw immediately
|
|
809
|
+
await keyringManager.trezorSigner.verifyUtxoAddress(
|
|
810
|
+
account.id,
|
|
811
|
+
'sys',
|
|
812
|
+
57
|
|
813
|
+
);
|
|
814
|
+
// Should not reach here
|
|
815
|
+
fail('Expected error was not thrown');
|
|
816
|
+
} catch (error: any) {
|
|
817
|
+
expect(error.message).toContain(
|
|
818
|
+
'Address verification cancelled by user'
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// Verify the mock was called
|
|
823
|
+
expect(
|
|
824
|
+
keyringManager.trezorSigner.verifyUtxoAddress
|
|
825
|
+
).toHaveBeenCalledWith(account.id, 'sys', 57);
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
});
|