@sidhujag/sysweb3-keyring 1.0.545 → 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.
Files changed (212) hide show
  1. package/coverage/clover.xml +2875 -0
  2. package/coverage/coverage-final.json +29468 -0
  3. package/coverage/lcov-report/base.css +354 -0
  4. package/coverage/lcov-report/block-navigation.js +85 -0
  5. package/coverage/lcov-report/favicon.png +0 -0
  6. package/coverage/lcov-report/index.html +320 -0
  7. package/coverage/lcov-report/prettify.css +101 -0
  8. package/coverage/lcov-report/prettify.js +1008 -0
  9. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  10. package/coverage/lcov-report/sorter.js +191 -0
  11. package/coverage/lcov-report/src/index.html +276 -0
  12. package/coverage/lcov-report/src/index.ts.html +114 -0
  13. package/coverage/lcov-report/src/initial-state.ts.html +558 -0
  14. package/coverage/lcov-report/src/keyring-manager.ts.html +6279 -0
  15. package/coverage/lcov-report/src/ledger/bitcoin_client/index.html +178 -0
  16. package/coverage/lcov-report/src/ledger/bitcoin_client/index.ts.html +144 -0
  17. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/appClient.ts.html +1560 -0
  18. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/bip32.ts.html +276 -0
  19. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/buffertools.ts.html +495 -0
  20. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/clientCommands.ts.html +1138 -0
  21. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/index.html +363 -0
  22. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts.html +289 -0
  23. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkle.ts.html +486 -0
  24. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkleMap.ts.html +240 -0
  25. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/policy.ts.html +342 -0
  26. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/psbtv2.ts.html +2388 -0
  27. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/varint.ts.html +453 -0
  28. package/coverage/lcov-report/src/ledger/consts.ts.html +177 -0
  29. package/coverage/lcov-report/src/ledger/index.html +216 -0
  30. package/coverage/lcov-report/src/ledger/index.ts.html +1371 -0
  31. package/coverage/lcov-report/src/ledger/utils.ts.html +102 -0
  32. package/coverage/lcov-report/src/signers.ts.html +591 -0
  33. package/coverage/lcov-report/src/storage.ts.html +198 -0
  34. package/coverage/lcov-report/src/transactions/ethereum.ts.html +5826 -0
  35. package/coverage/lcov-report/src/transactions/index.html +216 -0
  36. package/coverage/lcov-report/src/transactions/index.ts.html +93 -0
  37. package/coverage/lcov-report/src/transactions/syscoin.ts.html +1521 -0
  38. package/coverage/lcov-report/src/trezor/index.html +176 -0
  39. package/coverage/lcov-report/src/trezor/index.ts.html +2655 -0
  40. package/coverage/lcov-report/src/types.ts.html +1443 -0
  41. package/coverage/lcov-report/src/utils/derivation-paths.ts.html +486 -0
  42. package/coverage/lcov-report/src/utils/index.html +196 -0
  43. package/coverage/lcov-report/src/utils/psbt.ts.html +159 -0
  44. package/coverage/lcov-report/test/helpers/constants.ts.html +627 -0
  45. package/coverage/lcov-report/test/helpers/index.html +176 -0
  46. package/coverage/lcov.info +4832 -0
  47. package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/appClient.js +1 -124
  48. package/dist/cjs/ledger/bitcoin_client/lib/appClient.js.map +1 -0
  49. package/{cjs → dist/cjs}/transactions/ethereum.js +6 -2
  50. package/dist/cjs/transactions/ethereum.js.map +1 -0
  51. package/dist/package.json +50 -0
  52. package/{types → dist/types}/ledger/bitcoin_client/lib/appClient.d.ts +0 -6
  53. package/examples/basic-usage.js +140 -0
  54. package/jest.config.js +32 -0
  55. package/package.json +31 -13
  56. package/readme.md +201 -0
  57. package/src/declare.d.ts +7 -0
  58. package/src/errorUtils.ts +83 -0
  59. package/src/hardware-wallet-manager.ts +655 -0
  60. package/src/index.ts +12 -0
  61. package/src/initial-state.ts +108 -0
  62. package/src/keyring-manager.ts +2698 -0
  63. package/src/ledger/bitcoin_client/index.ts +19 -0
  64. package/src/ledger/bitcoin_client/lib/appClient.ts +405 -0
  65. package/src/ledger/bitcoin_client/lib/bip32.ts +61 -0
  66. package/src/ledger/bitcoin_client/lib/buffertools.ts +134 -0
  67. package/src/ledger/bitcoin_client/lib/clientCommands.ts +356 -0
  68. package/src/ledger/bitcoin_client/lib/constants.ts +12 -0
  69. package/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts +65 -0
  70. package/src/ledger/bitcoin_client/lib/merkle.ts +136 -0
  71. package/src/ledger/bitcoin_client/lib/merkleMap.ts +49 -0
  72. package/src/ledger/bitcoin_client/lib/policy.ts +91 -0
  73. package/src/ledger/bitcoin_client/lib/psbtv2.ts +768 -0
  74. package/src/ledger/bitcoin_client/lib/varint.ts +120 -0
  75. package/src/ledger/consts.ts +3 -0
  76. package/src/ledger/index.ts +685 -0
  77. package/src/ledger/types.ts +74 -0
  78. package/src/network-utils.ts +99 -0
  79. package/src/providers.ts +345 -0
  80. package/src/signers.ts +158 -0
  81. package/src/storage.ts +63 -0
  82. package/src/transactions/__tests__/integration.test.ts +303 -0
  83. package/src/transactions/__tests__/syscoin.test.ts +409 -0
  84. package/src/transactions/ethereum.ts +2503 -0
  85. package/src/transactions/index.ts +2 -0
  86. package/src/transactions/syscoin.ts +542 -0
  87. package/src/trezor/index.ts +1050 -0
  88. package/src/types.ts +366 -0
  89. package/src/utils/derivation-paths.ts +133 -0
  90. package/src/utils/psbt.ts +24 -0
  91. package/src/utils.ts +191 -0
  92. package/test/README.md +158 -0
  93. package/test/__mocks__/ledger-mock.js +20 -0
  94. package/test/__mocks__/trezor-mock.js +75 -0
  95. package/test/cleanup-summary.md +167 -0
  96. package/test/helpers/README.md +78 -0
  97. package/test/helpers/constants.ts +79 -0
  98. package/test/helpers/setup.ts +714 -0
  99. package/test/integration/import-validation.spec.ts +588 -0
  100. package/test/unit/hardware/ledger.spec.ts +869 -0
  101. package/test/unit/hardware/trezor.spec.ts +828 -0
  102. package/test/unit/keyring-manager/account-management.spec.ts +970 -0
  103. package/test/unit/keyring-manager/import-watchonly.spec.ts +181 -0
  104. package/test/unit/keyring-manager/import-wif.spec.ts +126 -0
  105. package/test/unit/keyring-manager/initialization.spec.ts +782 -0
  106. package/test/unit/keyring-manager/key-derivation.spec.ts +996 -0
  107. package/test/unit/keyring-manager/security.spec.ts +505 -0
  108. package/test/unit/keyring-manager/state-management.spec.ts +375 -0
  109. package/test/unit/network/network-management.spec.ts +372 -0
  110. package/test/unit/transactions/ethereum-transactions.spec.ts +382 -0
  111. package/test/unit/transactions/syscoin-transactions.spec.ts +615 -0
  112. package/tsconfig.json +14 -0
  113. package/cjs/ledger/bitcoin_client/lib/appClient.js.map +0 -1
  114. package/cjs/transactions/ethereum.js.map +0 -1
  115. /package/{README.md → dist/README.md} +0 -0
  116. /package/{cjs → dist/cjs}/errorUtils.js +0 -0
  117. /package/{cjs → dist/cjs}/errorUtils.js.map +0 -0
  118. /package/{cjs → dist/cjs}/hardware-wallet-manager.js +0 -0
  119. /package/{cjs → dist/cjs}/hardware-wallet-manager.js.map +0 -0
  120. /package/{cjs → dist/cjs}/index.js +0 -0
  121. /package/{cjs → dist/cjs}/index.js.map +0 -0
  122. /package/{cjs → dist/cjs}/initial-state.js +0 -0
  123. /package/{cjs → dist/cjs}/initial-state.js.map +0 -0
  124. /package/{cjs → dist/cjs}/keyring-manager.js +0 -0
  125. /package/{cjs → dist/cjs}/keyring-manager.js.map +0 -0
  126. /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js +0 -0
  127. /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js.map +0 -0
  128. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js +0 -0
  129. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js.map +0 -0
  130. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js +0 -0
  131. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js.map +0 -0
  132. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js +0 -0
  133. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js.map +0 -0
  134. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js +0 -0
  135. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js.map +0 -0
  136. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js +0 -0
  137. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js.map +0 -0
  138. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js +0 -0
  139. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js.map +0 -0
  140. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js +0 -0
  141. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js.map +0 -0
  142. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js +0 -0
  143. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js.map +0 -0
  144. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js +0 -0
  145. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js.map +0 -0
  146. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js +0 -0
  147. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js.map +0 -0
  148. /package/{cjs → dist/cjs}/ledger/consts.js +0 -0
  149. /package/{cjs → dist/cjs}/ledger/consts.js.map +0 -0
  150. /package/{cjs → dist/cjs}/ledger/index.js +0 -0
  151. /package/{cjs → dist/cjs}/ledger/index.js.map +0 -0
  152. /package/{cjs → dist/cjs}/ledger/types.js +0 -0
  153. /package/{cjs → dist/cjs}/ledger/types.js.map +0 -0
  154. /package/{cjs → dist/cjs}/network-utils.js +0 -0
  155. /package/{cjs → dist/cjs}/network-utils.js.map +0 -0
  156. /package/{cjs → dist/cjs}/providers.js +0 -0
  157. /package/{cjs → dist/cjs}/providers.js.map +0 -0
  158. /package/{cjs → dist/cjs}/signers.js +0 -0
  159. /package/{cjs → dist/cjs}/signers.js.map +0 -0
  160. /package/{cjs → dist/cjs}/storage.js +0 -0
  161. /package/{cjs → dist/cjs}/storage.js.map +0 -0
  162. /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js +0 -0
  163. /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js.map +0 -0
  164. /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js +0 -0
  165. /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js.map +0 -0
  166. /package/{cjs → dist/cjs}/transactions/index.js +0 -0
  167. /package/{cjs → dist/cjs}/transactions/index.js.map +0 -0
  168. /package/{cjs → dist/cjs}/transactions/syscoin.js +0 -0
  169. /package/{cjs → dist/cjs}/transactions/syscoin.js.map +0 -0
  170. /package/{cjs → dist/cjs}/trezor/index.js +0 -0
  171. /package/{cjs → dist/cjs}/trezor/index.js.map +0 -0
  172. /package/{cjs → dist/cjs}/types.js +0 -0
  173. /package/{cjs → dist/cjs}/types.js.map +0 -0
  174. /package/{cjs → dist/cjs}/utils/derivation-paths.js +0 -0
  175. /package/{cjs → dist/cjs}/utils/derivation-paths.js.map +0 -0
  176. /package/{cjs → dist/cjs}/utils/psbt.js +0 -0
  177. /package/{cjs → dist/cjs}/utils/psbt.js.map +0 -0
  178. /package/{cjs → dist/cjs}/utils.js +0 -0
  179. /package/{cjs → dist/cjs}/utils.js.map +0 -0
  180. /package/{types → dist/types}/errorUtils.d.ts +0 -0
  181. /package/{types → dist/types}/hardware-wallet-manager.d.ts +0 -0
  182. /package/{types → dist/types}/index.d.ts +0 -0
  183. /package/{types → dist/types}/initial-state.d.ts +0 -0
  184. /package/{types → dist/types}/keyring-manager.d.ts +0 -0
  185. /package/{types → dist/types}/ledger/bitcoin_client/index.d.ts +0 -0
  186. /package/{types → dist/types}/ledger/bitcoin_client/lib/bip32.d.ts +0 -0
  187. /package/{types → dist/types}/ledger/bitcoin_client/lib/buffertools.d.ts +0 -0
  188. /package/{types → dist/types}/ledger/bitcoin_client/lib/clientCommands.d.ts +0 -0
  189. /package/{types → dist/types}/ledger/bitcoin_client/lib/constants.d.ts +0 -0
  190. /package/{types → dist/types}/ledger/bitcoin_client/lib/merkelizedPsbt.d.ts +0 -0
  191. /package/{types → dist/types}/ledger/bitcoin_client/lib/merkle.d.ts +0 -0
  192. /package/{types → dist/types}/ledger/bitcoin_client/lib/merkleMap.d.ts +0 -0
  193. /package/{types → dist/types}/ledger/bitcoin_client/lib/policy.d.ts +0 -0
  194. /package/{types → dist/types}/ledger/bitcoin_client/lib/psbtv2.d.ts +0 -0
  195. /package/{types → dist/types}/ledger/bitcoin_client/lib/varint.d.ts +0 -0
  196. /package/{types → dist/types}/ledger/consts.d.ts +0 -0
  197. /package/{types → dist/types}/ledger/index.d.ts +0 -0
  198. /package/{types → dist/types}/ledger/types.d.ts +0 -0
  199. /package/{types → dist/types}/network-utils.d.ts +0 -0
  200. /package/{types → dist/types}/providers.d.ts +0 -0
  201. /package/{types → dist/types}/signers.d.ts +0 -0
  202. /package/{types → dist/types}/storage.d.ts +0 -0
  203. /package/{types → dist/types}/transactions/__tests__/integration.test.d.ts +0 -0
  204. /package/{types → dist/types}/transactions/__tests__/syscoin.test.d.ts +0 -0
  205. /package/{types → dist/types}/transactions/ethereum.d.ts +0 -0
  206. /package/{types → dist/types}/transactions/index.d.ts +0 -0
  207. /package/{types → dist/types}/transactions/syscoin.d.ts +0 -0
  208. /package/{types → dist/types}/trezor/index.d.ts +0 -0
  209. /package/{types → dist/types}/types.d.ts +0 -0
  210. /package/{types → dist/types}/utils/derivation-paths.d.ts +0 -0
  211. /package/{types → dist/types}/utils/psbt.d.ts +0 -0
  212. /package/{types → dist/types}/utils.d.ts +0 -0
@@ -0,0 +1,19 @@
1
+ /* eslint-disable import/no-named-as-default */
2
+ import AppClient, { PartialSignature } from './lib/appClient';
3
+ import {
4
+ DefaultDescriptorTemplate,
5
+ DefaultWalletPolicy,
6
+ WalletPolicy,
7
+ } from './lib/policy';
8
+ import { PsbtV2 } from './lib/psbtv2';
9
+
10
+ export {
11
+ AppClient,
12
+ PsbtV2,
13
+ DefaultDescriptorTemplate,
14
+ DefaultWalletPolicy,
15
+ PartialSignature,
16
+ WalletPolicy,
17
+ };
18
+
19
+ export default AppClient;
@@ -0,0 +1,405 @@
1
+ import Transport from '@ledgerhq/hw-transport';
2
+
3
+ import { pathElementsToBuffer, pathStringToArray } from './bip32';
4
+ import { ClientCommandInterpreter } from './clientCommands';
5
+ import { MerkelizedPsbt } from './merkelizedPsbt';
6
+ import { hashLeaf, Merkle } from './merkle';
7
+ import { WalletPolicy } from './policy';
8
+ import { PsbtV2 } from './psbtv2';
9
+ import { createVarint, parseVarint } from './varint';
10
+
11
+ const CLA_BTC = 0xe1;
12
+ const CLA_FRAMEWORK = 0xf8;
13
+
14
+ const CURRENT_PROTOCOL_VERSION = 1; // supported from version 2.1.0 of the app
15
+
16
+ enum BitcoinIns {
17
+ GET_PUBKEY = 0x00,
18
+ REGISTER_WALLET = 0x02,
19
+ GET_WALLET_ADDRESS = 0x03,
20
+ SIGN_PSBT = 0x04,
21
+ GET_MASTER_FINGERPRINT = 0x05,
22
+ SIGN_MESSAGE = 0x10,
23
+ }
24
+
25
+ enum FrameworkIns {
26
+ CONTINUE_INTERRUPTED = 0x01,
27
+ }
28
+
29
+ /**
30
+ * This class represents a partial signature produced by the app during signing.
31
+ * It always contains the `signature` and the corresponding `pubkey` whose private key
32
+ * was used for signing; in the case of taproot script paths, it also contains the
33
+ * tapleaf hash.
34
+ */
35
+ export class PartialSignature {
36
+ readonly pubkey: Buffer;
37
+ readonly signature: Buffer;
38
+ readonly tapleafHash?: Buffer;
39
+
40
+ constructor(pubkey: Buffer, signature: Buffer, tapleafHash?: Buffer) {
41
+ this.pubkey = pubkey;
42
+ this.signature = signature;
43
+ this.tapleafHash = tapleafHash;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Creates an instance of `PartialSignature` from the returned raw augmented pubkey and signature.
49
+ * @param pubkeyAugm the public key, concatenated with the tapleaf hash in the case of taproot script path spend.
50
+ * @param signature the signature
51
+ * @returns an instance of `PartialSignature`.
52
+ */
53
+ function makePartialSignature(
54
+ pubkeyAugm: Buffer,
55
+ signature: Buffer
56
+ ): PartialSignature {
57
+ if (pubkeyAugm.length == 64) {
58
+ // tapscript spend: concatenation of 32-bytes x-only pubkey and 32-bytes tapleaf_hash
59
+ return new PartialSignature(
60
+ pubkeyAugm.slice(0, 32),
61
+ signature,
62
+ pubkeyAugm.slice(32, 64)
63
+ );
64
+ } else if (pubkeyAugm.length == 32 || pubkeyAugm.length == 33) {
65
+ // legacy, segwit or taproot keypath spend: pubkeyAugm is just the pubkey
66
+ return new PartialSignature(pubkeyAugm, signature);
67
+ } else {
68
+ throw new Error(
69
+ `Invalid length for pubkeyAugm: ${pubkeyAugm.length} bytes.`
70
+ );
71
+ }
72
+ }
73
+
74
+ /**
75
+ * This class encapsulates the APDU protocol documented at
76
+ * https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
77
+ */
78
+ export class AppClient {
79
+ readonly transport: Transport;
80
+
81
+ constructor(transport: Transport) {
82
+ this.transport = transport;
83
+ }
84
+
85
+ private async makeRequest(
86
+ ins: BitcoinIns,
87
+ data: Buffer,
88
+ cci?: ClientCommandInterpreter
89
+ ): Promise<Buffer> {
90
+ let response: Buffer = await this.transport.send(
91
+ CLA_BTC,
92
+ ins,
93
+ 0,
94
+ CURRENT_PROTOCOL_VERSION,
95
+ data,
96
+ [0x9000, 0xe000]
97
+ );
98
+ while (response.readUInt16BE(response.length - 2) === 0xe000) {
99
+ if (!cci) {
100
+ throw new Error('Unexpected SW_INTERRUPTED_EXECUTION');
101
+ }
102
+
103
+ const hwRequest = response.slice(0, -2);
104
+ const commandResponse = cci.execute(hwRequest);
105
+
106
+ response = await this.transport.send(
107
+ CLA_FRAMEWORK,
108
+ FrameworkIns.CONTINUE_INTERRUPTED,
109
+ 0,
110
+ 0,
111
+ commandResponse,
112
+ [0x9000, 0xe000]
113
+ );
114
+ }
115
+ return response.slice(0, -2); // drop the status word (can only be 0x9000 at this point)
116
+ }
117
+
118
+ /**
119
+ * Returns an object containing the currently running app's name, version and the device status flags.
120
+ *
121
+ * @returns an object with app name, version and device status flags.
122
+ */
123
+ public async getAppAndVersion(): Promise<{
124
+ flags: number | Buffer;
125
+ name: string;
126
+ version: string;
127
+ }> {
128
+ const r = await this.transport.send(0xb0, 0x01, 0x00, 0x00);
129
+ let i = 0;
130
+ const format = r[i++];
131
+ if (format !== 1) throw new Error('Unexpected response');
132
+
133
+ const nameLength = r[i++];
134
+ const name = r.slice(i, (i += nameLength)).toString('ascii');
135
+ const versionLength = r[i++];
136
+ const version = r.slice(i, (i += versionLength)).toString('ascii');
137
+ const flagLength = r[i++];
138
+ const flags = r.slice(i, (i += flagLength));
139
+ return {
140
+ name,
141
+ version,
142
+ flags,
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Requests the BIP-32 extended pubkey to the hardware wallet.
148
+ * If `display` is `false`, only standard paths will be accepted; an error is returned if an unusual path is
149
+ * requested.
150
+ * If `display` is `true`, the requested path is shown on screen for user verification; unusual paths can be
151
+ * requested, and a warning is shown to the user in that case.
152
+ *
153
+ * @param path the requested BIP-32 path as a string
154
+ * @param display `false` to silently retrieve a pubkey for a standard path, `true` to display the path on screen
155
+ * @returns the base58-encoded serialized extended pubkey (xpub)
156
+ */
157
+ async getExtendedPubkey(path: string, display = false): Promise<string> {
158
+ const pathElements = pathStringToArray(path);
159
+ if (pathElements.length > 6) {
160
+ throw new Error('Path too long. At most 6 levels allowed.');
161
+ }
162
+ const response = await this.makeRequest(
163
+ BitcoinIns.GET_PUBKEY,
164
+ Buffer.concat([
165
+ Buffer.from(display ? [1] : [0]),
166
+ pathElementsToBuffer(pathElements),
167
+ ])
168
+ );
169
+ return response.toString('ascii');
170
+ }
171
+
172
+ /**
173
+ * Registers a `WalletPolicy`, after interactive verification from the user.
174
+ * On success, after user's approval, this function returns the id (which is the same that can be computed with
175
+ * `walletPolicy.getid()`), followed by the 32-byte hmac. The client should store the hmac to use it for future
176
+ * requests to `getWalletAddress` or `signPsbt` using this `WalletPolicy`.
177
+ *
178
+ * @param walletPolicy the `WalletPolicy` to register
179
+ * @returns a pair of two 32-byte arrays: the id of the Wallet Policy, followed by the policy hmac
180
+ */
181
+ async registerWallet(
182
+ walletPolicy: WalletPolicy
183
+ ): Promise<readonly [Buffer, Buffer]> {
184
+ const clientInterpreter = new ClientCommandInterpreter();
185
+
186
+ clientInterpreter.addKnownWalletPolicy(walletPolicy);
187
+
188
+ const serializedWalletPolicy = walletPolicy.serialize();
189
+ const response = await this.makeRequest(
190
+ BitcoinIns.REGISTER_WALLET,
191
+ Buffer.concat([
192
+ createVarint(serializedWalletPolicy.length),
193
+ serializedWalletPolicy,
194
+ ]),
195
+ clientInterpreter
196
+ );
197
+
198
+ if (response.length != 64) {
199
+ throw Error(
200
+ `Invalid response length. Expected 64 bytes, got ${response.length}`
201
+ );
202
+ }
203
+ const walletId = response.subarray(0, 32);
204
+ const walletHMAC = response.subarray(32);
205
+
206
+ // Note: Address validation removed - trusting Ledger device for address generation
207
+
208
+ return [walletId, walletHMAC];
209
+ }
210
+
211
+ /**
212
+ * Returns the address of `walletPolicy` for the given `change` and `addressIndex`.
213
+ *
214
+ * @param walletPolicy the `WalletPolicy` to use
215
+ * @param walletHMAC the 32-byte hmac returned during wallet registration for a registered policy; otherwise
216
+ * `null` for a standard policy
217
+ * @param change `0` for a normal receive address, `1` for a change address
218
+ * @param addressIndex the address index to retrieve
219
+ * @param display `True` to show the address on screen, `False` to retrieve it silently
220
+ * @returns the address, as an ascii string.
221
+ */
222
+ async getWalletAddress(
223
+ walletPolicy: WalletPolicy,
224
+ walletHMAC: Buffer | null,
225
+ change: number,
226
+ addressIndex: number,
227
+ display: boolean
228
+ ): Promise<string> {
229
+ if (change !== 0 && change !== 1)
230
+ throw new Error('Change can only be 0 or 1');
231
+ if (addressIndex < 0 || !Number.isInteger(addressIndex))
232
+ throw new Error('Invalid address index');
233
+
234
+ if (walletHMAC != null && walletHMAC.length != 32) {
235
+ throw new Error('Invalid HMAC length');
236
+ }
237
+
238
+ const clientInterpreter = new ClientCommandInterpreter();
239
+ // Provide wallet policy artifacts to the interpreter
240
+ clientInterpreter.addKnownWalletPolicy(walletPolicy);
241
+
242
+ const addressIndexBuffer = Buffer.alloc(4);
243
+ addressIndexBuffer.writeUInt32BE(addressIndex, 0);
244
+
245
+ const response = await this.makeRequest(
246
+ BitcoinIns.GET_WALLET_ADDRESS,
247
+ Buffer.concat([
248
+ Buffer.from(display ? [1] : [0]),
249
+ walletPolicy.getId(),
250
+ walletHMAC || Buffer.alloc(32, 0),
251
+ Buffer.from([change]),
252
+ addressIndexBuffer,
253
+ ]),
254
+ clientInterpreter
255
+ );
256
+
257
+ const address = response.toString('ascii');
258
+ return address;
259
+ }
260
+
261
+ /**
262
+ * Signs a psbt using a (standard or registered) `WalletPolicy`. This is an interactive command, as user validation
263
+ * is necessary using the device's secure screen.
264
+ * On success, a map of input indexes and signatures is returned.
265
+ * @param psbt a base64-encoded string, or a psbt in a binary Buffer. Using the `PsbtV2` type is deprecated.
266
+ * @param walletPolicy the `WalletPolicy` to use for signing
267
+ * @param walletHMAC the 32-byte hmac obtained during wallet policy registration, or `null` for a standard policy
268
+ * @param progressCallback optionally, a callback that will be called every time a signature is produced during
269
+ * the signing process. The callback does not receive any argument, but can be used to track progress.
270
+ * @returns an array of of tuples with 2 elements containing:
271
+ * - the index of the input being signed;
272
+ * - an instance of PartialSignature
273
+ */
274
+ async signPsbt(
275
+ psbt: PsbtV2 | string | Buffer,
276
+ walletPolicy: WalletPolicy,
277
+ walletHMAC: Buffer | null,
278
+ progressCallback?: () => void
279
+ ): Promise<[number, PartialSignature][]> {
280
+ if (typeof psbt === 'string') {
281
+ psbt = Buffer.from(psbt, 'base64');
282
+ }
283
+
284
+ if (Buffer.isBuffer(psbt)) {
285
+ const psbtObj = new PsbtV2();
286
+ psbtObj.deserialize(psbt);
287
+ psbt = psbtObj;
288
+ }
289
+
290
+ const merkelizedPsbt = new MerkelizedPsbt(psbt);
291
+
292
+ if (walletHMAC != null && walletHMAC.length != 32) {
293
+ throw new Error('Invalid HMAC length');
294
+ }
295
+
296
+ const clientInterpreter = new ClientCommandInterpreter(progressCallback);
297
+
298
+ // prepare ClientCommandInterpreter
299
+ clientInterpreter.addKnownWalletPolicy(walletPolicy);
300
+
301
+ clientInterpreter.addKnownMapping(merkelizedPsbt.globalMerkleMap);
302
+ for (const map of merkelizedPsbt.inputMerkleMaps) {
303
+ clientInterpreter.addKnownMapping(map);
304
+ }
305
+ for (const map of merkelizedPsbt.outputMerkleMaps) {
306
+ clientInterpreter.addKnownMapping(map);
307
+ }
308
+
309
+ clientInterpreter.addKnownList(merkelizedPsbt.inputMapCommitments);
310
+ const inputMapsRoot = new Merkle(
311
+ merkelizedPsbt.inputMapCommitments.map((m) => hashLeaf(m))
312
+ ).getRoot();
313
+ clientInterpreter.addKnownList(merkelizedPsbt.outputMapCommitments);
314
+ const outputMapsRoot = new Merkle(
315
+ merkelizedPsbt.outputMapCommitments.map((m) => hashLeaf(m))
316
+ ).getRoot();
317
+
318
+ await this.makeRequest(
319
+ BitcoinIns.SIGN_PSBT,
320
+ Buffer.concat([
321
+ merkelizedPsbt.getGlobalKeysValuesRoot(),
322
+ createVarint(merkelizedPsbt.getGlobalInputCount()),
323
+ inputMapsRoot,
324
+ createVarint(merkelizedPsbt.getGlobalOutputCount()),
325
+ outputMapsRoot,
326
+ walletPolicy.getId(),
327
+ walletHMAC || Buffer.alloc(32, 0),
328
+ ]),
329
+ clientInterpreter
330
+ );
331
+
332
+ const yielded = clientInterpreter.getYielded();
333
+
334
+ const ret: [number, PartialSignature][] = [];
335
+ for (const inputAndSig of yielded) {
336
+ // inputAndSig contains:
337
+ // <inputIndex : varint> <pubkeyLen : 1 byte> <pubkey : pubkeyLen bytes (32 or 33)> <signature : variable length>
338
+ const [inputIndex, inputIndexLen] = parseVarint(inputAndSig, 0);
339
+ const pubkeyAugmLen = inputAndSig[inputIndexLen];
340
+ const pubkeyAugm = inputAndSig.subarray(
341
+ inputIndexLen + 1,
342
+ inputIndexLen + 1 + pubkeyAugmLen
343
+ );
344
+ const signature = inputAndSig.subarray(inputIndexLen + 1 + pubkeyAugmLen);
345
+
346
+ const partialSig = makePartialSignature(pubkeyAugm, signature);
347
+
348
+ ret.push([Number(inputIndex), partialSig]);
349
+ }
350
+ return ret;
351
+ }
352
+
353
+ /**
354
+ * Returns the fingerprint of the master public key, as per BIP-32 standard.
355
+ * @returns the master key fingerprint as a string of 8 hexadecimal digits.
356
+ */
357
+ async getMasterFingerprint(): Promise<string> {
358
+ const fpr = await this.makeRequest(
359
+ BitcoinIns.GET_MASTER_FINGERPRINT,
360
+ Buffer.from([])
361
+ );
362
+ return fpr.toString('hex');
363
+ }
364
+
365
+ /**
366
+ * Signs a message using the legacy Bitcoin Message Signing standard. The signed message is
367
+ * the double-sha256 hash of the concatenation of:
368
+ * - "\x18Bitcoin Signed Message:\n";
369
+ * - the length of `message`, encoded as a Bitcoin-style variable length integer;
370
+ * - `message`.
371
+ *
372
+ * @param message the serialized message to sign
373
+ * @param path the BIP-32 path of the key used to sign the message
374
+ * @returns base64-encoded signature of the message.
375
+ */
376
+ async signMessage(message: Buffer, path: string): Promise<string> {
377
+ const pathElements = pathStringToArray(path);
378
+
379
+ const clientInterpreter = new ClientCommandInterpreter();
380
+
381
+ // prepare ClientCommandInterpreter
382
+ const nChunks = Math.ceil(message.length / 64);
383
+ const chunks: Buffer[] = [];
384
+ for (let i = 0; i < nChunks; i++) {
385
+ chunks.push(message.subarray(64 * i, 64 * i + 64));
386
+ }
387
+
388
+ clientInterpreter.addKnownList(chunks);
389
+ const chunksRoot = new Merkle(chunks.map((m) => hashLeaf(m))).getRoot();
390
+
391
+ const result = await this.makeRequest(
392
+ BitcoinIns.SIGN_MESSAGE,
393
+ Buffer.concat([
394
+ pathElementsToBuffer(pathElements),
395
+ createVarint(message.length),
396
+ chunksRoot,
397
+ ]),
398
+ clientInterpreter
399
+ );
400
+
401
+ return result.toString('base64');
402
+ }
403
+ }
404
+
405
+ export default AppClient;
@@ -0,0 +1,61 @@
1
+ import bippath from 'bip32-path';
2
+ import bs58check from 'bs58check';
3
+
4
+ export function pathElementsToBuffer(paths: readonly number[]): Buffer {
5
+ const buffer = Buffer.alloc(1 + paths.length * 4);
6
+ buffer[0] = paths.length;
7
+ paths.forEach((element, index) => {
8
+ buffer.writeUInt32BE(element, 1 + 4 * index);
9
+ });
10
+ return buffer;
11
+ }
12
+
13
+ export function bip32asBuffer(path: string): Buffer {
14
+ const pathElements = !path ? [] : pathStringToArray(path);
15
+ return pathElementsToBuffer(pathElements);
16
+ }
17
+
18
+ export function pathArrayToString(pathElements: readonly number[]): string {
19
+ // bippath doesn't handle an empty path.
20
+ if (pathElements.length == 0) {
21
+ return 'm';
22
+ }
23
+ return bippath.fromPathArray(pathElements).toString();
24
+ }
25
+
26
+ export function pathStringToArray(path: string): readonly number[] {
27
+ // bippath doesn't handle an empty path.
28
+ if (path == 'm' || path == '') {
29
+ return [];
30
+ }
31
+ return bippath.fromString(path).toPathArray();
32
+ }
33
+
34
+ export function pubkeyFromXpub(xpub: string): Buffer {
35
+ const xpubBuf = Buffer.from(bs58check.decode(xpub));
36
+ return xpubBuf.slice(xpubBuf.length - 33);
37
+ }
38
+
39
+ export function getXpubComponents(xpub: string): {
40
+ readonly chaincode: Buffer;
41
+ readonly pubkey: Buffer;
42
+ readonly version: number;
43
+ } {
44
+ const xpubBuf = Buffer.from(bs58check.decode(xpub));
45
+ return {
46
+ chaincode: xpubBuf.slice(13, 13 + 32),
47
+ pubkey: xpubBuf.slice(xpubBuf.length - 33),
48
+ version: xpubBuf.readUInt32BE(0),
49
+ };
50
+ }
51
+
52
+ export function hardenedPathOf(
53
+ pathElements: readonly number[]
54
+ ): readonly number[] {
55
+ for (let i = pathElements.length - 1; i >= 0; i--) {
56
+ if (pathElements[i] >= 0x80000000) {
57
+ return pathElements.slice(0, i + 1);
58
+ }
59
+ }
60
+ return [];
61
+ }
@@ -0,0 +1,134 @@
1
+ import { createVarint, parseVarint, sanitizeBigintToNumber } from './varint';
2
+
3
+ export function unsafeTo64bitLE(n: number): Buffer {
4
+ // we want to represent the input as a 8-bytes array
5
+ if (n > Number.MAX_SAFE_INTEGER) {
6
+ throw new Error("Can't convert numbers > MAX_SAFE_INT");
7
+ }
8
+ const byteArray = Buffer.alloc(8, 0);
9
+ for (let index = 0; index < byteArray.length; index++) {
10
+ const byte = n & 0xff;
11
+ byteArray[index] = byte;
12
+ n = (n - byte) / 256;
13
+ }
14
+ return byteArray;
15
+ }
16
+
17
+ export function unsafeFrom64bitLE(byteArray: Buffer): number {
18
+ let value = 0;
19
+ if (byteArray.length != 8) {
20
+ throw new Error('Expected Bufffer of lenght 8');
21
+ }
22
+ if (byteArray[7] != 0) {
23
+ throw new Error("Can't encode numbers > MAX_SAFE_INT");
24
+ }
25
+ if (byteArray[6] > 0x1f) {
26
+ throw new Error("Can't encode numbers > MAX_SAFE_INT");
27
+ }
28
+ for (let i = byteArray.length - 1; i >= 0; i--) {
29
+ value = value * 256 + byteArray[i];
30
+ }
31
+ return value;
32
+ }
33
+
34
+ export class BufferWriter {
35
+ private bufs: Buffer[] = [];
36
+
37
+ write(alloc: number, fn: (b: Buffer) => void): void {
38
+ const b = Buffer.alloc(alloc);
39
+ fn(b);
40
+ this.bufs.push(b);
41
+ }
42
+
43
+ writeUInt8(i: number): void {
44
+ this.write(1, (b) => b.writeUInt8(i, 0));
45
+ }
46
+
47
+ writeInt32(i: number): void {
48
+ this.write(4, (b) => b.writeInt32LE(i, 0));
49
+ }
50
+
51
+ writeUInt32(i: number): void {
52
+ this.write(4, (b) => b.writeUInt32LE(i, 0));
53
+ }
54
+
55
+ writeUInt64(i: number): void {
56
+ const bytes = unsafeTo64bitLE(i);
57
+ this.writeSlice(bytes);
58
+ }
59
+
60
+ writeVarInt(i: number): void {
61
+ this.bufs.push(createVarint(i));
62
+ }
63
+
64
+ writeSlice(slice: Buffer): void {
65
+ this.bufs.push(Buffer.from(slice));
66
+ }
67
+
68
+ writeVarSlice(slice: Buffer): void {
69
+ this.writeVarInt(slice.length);
70
+ this.writeSlice(slice);
71
+ }
72
+
73
+ buffer(): Buffer {
74
+ return Buffer.concat(this.bufs);
75
+ }
76
+ }
77
+
78
+ export class BufferReader {
79
+ constructor(public readonly buffer: Buffer, public offset: number = 0) {}
80
+
81
+ available(): number {
82
+ return this.buffer.length - this.offset;
83
+ }
84
+
85
+ readUInt8(): number {
86
+ const result = this.buffer.readUInt8(this.offset);
87
+ this.offset++;
88
+ return result;
89
+ }
90
+
91
+ readInt32(): number {
92
+ const result = this.buffer.readInt32LE(this.offset);
93
+ this.offset += 4;
94
+ return result;
95
+ }
96
+
97
+ readUInt32(): number {
98
+ const result = this.buffer.readUInt32LE(this.offset);
99
+ this.offset += 4;
100
+ return result;
101
+ }
102
+
103
+ readUInt64(): number {
104
+ const buf = this.readSlice(8);
105
+ return unsafeFrom64bitLE(buf);
106
+ }
107
+
108
+ readVarInt(): bigint {
109
+ const [vi, viSize] = parseVarint(this.buffer, this.offset);
110
+ this.offset += viSize;
111
+ return vi;
112
+ }
113
+
114
+ readSlice(n: number): Buffer {
115
+ if (this.buffer.length < this.offset + n) {
116
+ throw new Error('Cannot read slice out of bounds');
117
+ }
118
+ const result = this.buffer.slice(this.offset, this.offset + n);
119
+ this.offset += n;
120
+ return result;
121
+ }
122
+
123
+ readVarSlice(): Buffer {
124
+ const n = sanitizeBigintToNumber(this.readVarInt());
125
+ return this.readSlice(n);
126
+ }
127
+
128
+ readVector(): readonly Buffer[] {
129
+ const count = this.readVarInt();
130
+ const vector: Buffer[] = [];
131
+ for (let i = 0; i < count; i++) vector.push(this.readVarSlice());
132
+ return vector;
133
+ }
134
+ }