@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.
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 +24 -11
  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,356 @@
1
+ /* eslint-disable camelcase */
2
+ import CryptoJS from 'crypto-js';
3
+
4
+ // Helper function for SHA256 hashing (browser-compatible)
5
+ function sha256(data: Buffer): Buffer {
6
+ const wordArray = CryptoJS.lib.WordArray.create(data);
7
+ const hash = CryptoJS.SHA256(wordArray);
8
+ const hashBuffer = Buffer.from(hash.toString(CryptoJS.enc.Hex), 'hex');
9
+ return hashBuffer;
10
+ }
11
+
12
+ import { BufferReader } from './buffertools';
13
+ import { hashLeaf, Merkle } from './merkle';
14
+ import { MerkleMap } from './merkleMap';
15
+ import { WalletPolicy } from './policy';
16
+ import { createVarint, sanitizeBigintToNumber } from './varint';
17
+
18
+ enum ClientCommandCode {
19
+ YIELD = 0x10,
20
+ GET_PREIMAGE = 0x40,
21
+ GET_MERKLE_LEAF_PROOF = 0x41,
22
+ GET_MERKLE_LEAF_INDEX = 0x42,
23
+ GET_MORE_ELEMENTS = 0xa0,
24
+ }
25
+
26
+ abstract class ClientCommand {
27
+ abstract code: ClientCommandCode;
28
+ abstract execute(request: Buffer): Buffer;
29
+ }
30
+
31
+ export class YieldCommand extends ClientCommand {
32
+ private results: Buffer[];
33
+
34
+ readonly code = ClientCommandCode.YIELD;
35
+
36
+ constructor(
37
+ results: Buffer[],
38
+ private readonly progressCallback?: () => void
39
+ ) {
40
+ super();
41
+ this.results = results;
42
+ }
43
+
44
+ execute(request: Buffer): Buffer {
45
+ this.results.push(Buffer.from(request.subarray(1)));
46
+ if (this.progressCallback) {
47
+ this.progressCallback();
48
+ }
49
+ return Buffer.from('');
50
+ }
51
+ }
52
+
53
+ export class GetPreimageCommand extends ClientCommand {
54
+ private readonly known_preimages: ReadonlyMap<string, Buffer>;
55
+ private queue: Buffer[];
56
+
57
+ readonly code = ClientCommandCode.GET_PREIMAGE;
58
+
59
+ constructor(known_preimages: ReadonlyMap<string, Buffer>, queue: Buffer[]) {
60
+ super();
61
+ this.known_preimages = known_preimages;
62
+ this.queue = queue;
63
+ }
64
+
65
+ execute(request: Buffer): Buffer {
66
+ const req = Buffer.from(request.subarray(1));
67
+
68
+ // we expect no more data to read
69
+ if (req.length != 1 + 32) {
70
+ throw new Error('Invalid request, unexpected trailing data');
71
+ }
72
+
73
+ if (req[0] != 0) {
74
+ throw new Error('Unsupported request, the first byte should be 0');
75
+ }
76
+
77
+ // read the hash
78
+ const hash = Buffer.alloc(32);
79
+ for (let i = 0; i < 32; i++) {
80
+ hash[i] = req[1 + i];
81
+ }
82
+ const req_hash_hex = hash.toString('hex');
83
+
84
+ const known_preimage = this.known_preimages.get(req_hash_hex);
85
+ if (known_preimage != undefined) {
86
+ const preimage_len_varint = createVarint(known_preimage.length);
87
+
88
+ // We can send at most 255 - len(preimage_len_out) - 1 bytes in a single message;
89
+ // the rest will be stored in the queue for GET_MORE_ELEMENTS
90
+ const max_payload_size = 255 - preimage_len_varint.length - 1;
91
+
92
+ const payload_size = Math.min(max_payload_size, known_preimage.length);
93
+
94
+ if (payload_size < known_preimage.length) {
95
+ for (let i = payload_size; i < known_preimage.length; i++) {
96
+ this.queue.push(Buffer.from([known_preimage[i]]));
97
+ }
98
+ }
99
+
100
+ return Buffer.concat([
101
+ preimage_len_varint,
102
+ Buffer.from([payload_size]),
103
+ Buffer.from(known_preimage.subarray(0, payload_size)),
104
+ ]);
105
+ }
106
+
107
+ throw Error(`Requested unknown preimage for: ${req_hash_hex}`);
108
+ }
109
+ }
110
+
111
+ export class GetMerkleLeafProofCommand extends ClientCommand {
112
+ private readonly known_trees: ReadonlyMap<string, Merkle>;
113
+ private queue: Buffer[];
114
+
115
+ readonly code = ClientCommandCode.GET_MERKLE_LEAF_PROOF;
116
+
117
+ constructor(known_trees: ReadonlyMap<string, Merkle>, queue: Buffer[]) {
118
+ super();
119
+ this.known_trees = known_trees;
120
+ this.queue = queue;
121
+ }
122
+
123
+ execute(request: Buffer): Buffer {
124
+ const req = Buffer.from(request.subarray(1));
125
+
126
+ if (req.length < 32 + 1 + 1) {
127
+ throw new Error('Invalid request, expected at least 34 bytes');
128
+ }
129
+
130
+ const reqBuf = new BufferReader(req);
131
+ const hash = reqBuf.readSlice(32);
132
+ const hash_hex = hash.toString('hex');
133
+
134
+ let tree_size: number;
135
+ let leaf_index: number;
136
+ try {
137
+ tree_size = sanitizeBigintToNumber(reqBuf.readVarInt());
138
+ leaf_index = sanitizeBigintToNumber(reqBuf.readVarInt());
139
+ } catch (e) {
140
+ throw new Error(
141
+ "Invalid request, couldn't parse tree_size or leaf_index"
142
+ );
143
+ }
144
+
145
+ const mt = this.known_trees.get(hash_hex);
146
+ if (!mt) {
147
+ throw Error(`Requested Merkle leaf proof for unknown tree: ${hash_hex}`);
148
+ }
149
+
150
+ if (leaf_index >= tree_size || mt.size() != tree_size) {
151
+ throw Error('Invalid index or tree size.');
152
+ }
153
+
154
+ if (this.queue.length != 0) {
155
+ throw Error(
156
+ 'This command should not execute when the queue is not empty.'
157
+ );
158
+ }
159
+
160
+ const proof = mt.getProof(leaf_index);
161
+
162
+ const n_response_elements = Math.min(
163
+ Math.floor((255 - 32 - 1 - 1) / 32),
164
+ proof.length
165
+ );
166
+ const n_leftover_elements = proof.length - n_response_elements;
167
+
168
+ // Add to the queue any proof elements that do not fit the response
169
+ if (n_leftover_elements > 0) {
170
+ this.queue.push(...proof.slice(-n_leftover_elements));
171
+ }
172
+
173
+ return Buffer.concat([
174
+ mt.getLeafHash(leaf_index),
175
+ Buffer.from([proof.length]),
176
+ Buffer.from([n_response_elements]),
177
+ ...proof.slice(0, n_response_elements),
178
+ ]);
179
+ }
180
+ }
181
+
182
+ export class GetMerkleLeafIndexCommand extends ClientCommand {
183
+ private readonly known_trees: ReadonlyMap<string, Merkle>;
184
+
185
+ readonly code = ClientCommandCode.GET_MERKLE_LEAF_INDEX;
186
+
187
+ constructor(known_trees: ReadonlyMap<string, Merkle>) {
188
+ super();
189
+ this.known_trees = known_trees;
190
+ }
191
+
192
+ execute(request: Buffer): Buffer {
193
+ const req = Buffer.from(request.subarray(1));
194
+
195
+ if (req.length != 32 + 32) {
196
+ throw new Error('Invalid request, unexpected trailing data');
197
+ }
198
+
199
+ // read the root hash
200
+ const root_hash = Buffer.alloc(32);
201
+ for (let i = 0; i < 32; i++) {
202
+ root_hash[i] = req.readUInt8(i);
203
+ }
204
+ const root_hash_hex = root_hash.toString('hex');
205
+
206
+ // read the leaf hash
207
+ const leef_hash = Buffer.alloc(32);
208
+ for (let i = 0; i < 32; i++) {
209
+ leef_hash[i] = req.readUInt8(32 + i);
210
+ }
211
+ const leef_hash_hex = leef_hash.toString('hex');
212
+
213
+ const mt = this.known_trees.get(root_hash_hex);
214
+ if (!mt) {
215
+ throw Error(
216
+ `Requested Merkle leaf index for unknown root: ${root_hash_hex}`
217
+ );
218
+ }
219
+
220
+ let leaf_index = 0;
221
+ let found = 0;
222
+ for (let i = 0; i < mt.size(); i++) {
223
+ if (mt.getLeafHash(i).toString('hex') == leef_hash_hex) {
224
+ found = 1;
225
+ leaf_index = i;
226
+ break;
227
+ }
228
+ }
229
+ return Buffer.concat([Buffer.from([found]), createVarint(leaf_index)]);
230
+ }
231
+ }
232
+
233
+ export class GetMoreElementsCommand extends ClientCommand {
234
+ queue: Buffer[];
235
+
236
+ readonly code = ClientCommandCode.GET_MORE_ELEMENTS;
237
+
238
+ constructor(queue: Buffer[]) {
239
+ super();
240
+ this.queue = queue;
241
+ }
242
+
243
+ execute(request: Buffer): Buffer {
244
+ if (request.length != 1) {
245
+ throw new Error('Invalid request, unexpected trailing data');
246
+ }
247
+
248
+ if (this.queue.length === 0) {
249
+ throw new Error('No elements to get');
250
+ }
251
+
252
+ // all elements should have the same length
253
+ const element_len = this.queue[0].length;
254
+ if (this.queue.some((el) => el.length != element_len)) {
255
+ throw new Error(
256
+ 'The queue contains elements with different byte length, which is not expected'
257
+ );
258
+ }
259
+
260
+ const max_elements = Math.floor(253 / element_len);
261
+ const n_returned_elements = Math.min(max_elements, this.queue.length);
262
+
263
+ const returned_elements = this.queue.splice(0, n_returned_elements);
264
+
265
+ return Buffer.concat([
266
+ Buffer.from([n_returned_elements]),
267
+ Buffer.from([element_len]),
268
+ ...returned_elements,
269
+ ]);
270
+ }
271
+ }
272
+
273
+ /**
274
+ * This class will dispatch a client command coming from the hardware device to
275
+ * the appropriate client command implementation. Those client commands
276
+ * typically requests data from a merkle tree or merkelized maps.
277
+ *
278
+ * A ClientCommandInterpreter is prepared by adding the merkle trees and
279
+ * merkelized maps it should be able to serve to the hardware device. This class
280
+ * doesn't know anything about the semantics of the data it holds, it just
281
+ * serves merkle data. It doesn't even know in what context it is being
282
+ * executed, ie SignPsbt, getWalletAddress, etc.
283
+ *
284
+ * If the command yelds results to the client, as signPsbt does, the yielded
285
+ * data will be accessible after the command completed by calling getYielded(),
286
+ * which will return the yields in the same order as they came in.
287
+ */
288
+ export class ClientCommandInterpreter {
289
+ private readonly roots: Map<string, Merkle> = new Map();
290
+ private readonly preimages: Map<string, Buffer> = new Map();
291
+
292
+ private yielded: Buffer[] = [];
293
+
294
+ private queue: Buffer[] = [];
295
+
296
+ private readonly commands: Map<ClientCommandCode, ClientCommand> = new Map();
297
+
298
+ constructor(progressCallback?: () => void) {
299
+ const commands = [
300
+ new YieldCommand(this.yielded, progressCallback),
301
+ new GetPreimageCommand(this.preimages, this.queue),
302
+ new GetMerkleLeafIndexCommand(this.roots),
303
+ new GetMerkleLeafProofCommand(this.roots, this.queue),
304
+ new GetMoreElementsCommand(this.queue),
305
+ ];
306
+
307
+ for (const cmd of commands) {
308
+ if (this.commands.has(cmd.code)) {
309
+ throw new Error(`Multiple commands with code ${cmd.code}`);
310
+ }
311
+ this.commands.set(cmd.code, cmd);
312
+ }
313
+ }
314
+
315
+ getYielded(): readonly Buffer[] {
316
+ return this.yielded;
317
+ }
318
+
319
+ addKnownPreimage(preimage: Buffer): void {
320
+ this.preimages.set(sha256(preimage).toString('hex'), preimage);
321
+ }
322
+
323
+ addKnownList(elements: readonly Buffer[]): void {
324
+ for (const el of elements) {
325
+ const preimage = Buffer.concat([Buffer.from([0]), el]);
326
+ this.addKnownPreimage(preimage);
327
+ }
328
+ const mt = new Merkle(elements.map((el) => hashLeaf(el)));
329
+ this.roots.set(mt.getRoot().toString('hex'), mt);
330
+ }
331
+
332
+ addKnownMapping(mm: MerkleMap): void {
333
+ this.addKnownList(mm.keys);
334
+ this.addKnownList(mm.values);
335
+ }
336
+
337
+ addKnownWalletPolicy(wp: WalletPolicy): void {
338
+ this.addKnownPreimage(wp.serialize());
339
+ this.addKnownList(wp.keys.map((k) => Buffer.from(k, 'ascii')));
340
+ this.addKnownPreimage(Buffer.from(wp.descriptorTemplate));
341
+ }
342
+
343
+ execute(request: Buffer): Buffer {
344
+ if (request.length == 0) {
345
+ throw new Error('Unexpected empty command');
346
+ }
347
+
348
+ const cmdCode = request[0];
349
+ const cmd = this.commands.get(cmdCode);
350
+ if (!cmd) {
351
+ throw new Error(`Unexpected command code ${cmdCode}`);
352
+ }
353
+
354
+ return cmd.execute(request);
355
+ }
356
+ }
@@ -0,0 +1,12 @@
1
+ export const MAX_SCRIPT_BLOCK = 50;
2
+ export const DEFAULT_VERSION = 1;
3
+ export const DEFAULT_LOCKTIME = 0;
4
+ export const DEFAULT_SEQUENCE = 0xffffffff;
5
+ export const SIGHASH_ALL = 1;
6
+ export const OP_DUP = 0x76;
7
+ export const OP_HASH160 = 0xa9;
8
+ export const HASH_SIZE = 0x14;
9
+ export const OP_EQUAL = 0x87;
10
+ export const OP_EQUALVERIFY = 0x88;
11
+ export const OP_CHECKSIG = 0xac;
12
+ export const OP_RETURN = 0x6a;
@@ -0,0 +1,65 @@
1
+ import { MerkleMap } from './merkleMap';
2
+ import { PsbtV2 } from './psbtv2';
3
+
4
+ /**
5
+ * This class merkelizes a PSBTv2, by merkelizing the different
6
+ * maps of the psbt. This is used during the transaction signing process,
7
+ * where the hardware app can request specific parts of the psbt from the
8
+ * client code and be sure that the response data actually belong to the psbt.
9
+ * The reason for this is the limited amount of memory available to the app,
10
+ * so it can't always store the full psbt in memory.
11
+ *
12
+ * The signing process is documented at
13
+ * https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#sign_psbt
14
+ */
15
+ export class MerkelizedPsbt extends PsbtV2 {
16
+ public readonly globalMerkleMap: MerkleMap;
17
+ public inputMerkleMaps: MerkleMap[] = [];
18
+ public outputMerkleMaps: MerkleMap[] = [];
19
+ public inputMapCommitments: Buffer[];
20
+ public outputMapCommitments: Buffer[];
21
+ constructor(psbt: PsbtV2) {
22
+ super();
23
+ psbt.copy(this);
24
+ this.globalMerkleMap = MerkelizedPsbt.createMerkleMap(this.globalMap);
25
+
26
+ for (let i = 0; i < this.getGlobalInputCount(); i++) {
27
+ this.inputMerkleMaps.push(
28
+ MerkelizedPsbt.createMerkleMap(this.inputMaps[i])
29
+ );
30
+ }
31
+ this.inputMapCommitments = [...this.inputMerkleMaps.values()].map((v) =>
32
+ v.commitment()
33
+ );
34
+
35
+ for (let i = 0; i < this.getGlobalOutputCount(); i++) {
36
+ this.outputMerkleMaps.push(
37
+ MerkelizedPsbt.createMerkleMap(this.outputMaps[i])
38
+ );
39
+ }
40
+ this.outputMapCommitments = [...this.outputMerkleMaps.values()].map((v) =>
41
+ v.commitment()
42
+ );
43
+ }
44
+ // These public functions are for MerkelizedPsbt.
45
+ getGlobalSize(): number {
46
+ return this.globalMap.size;
47
+ }
48
+ getGlobalKeysValuesRoot(): Buffer {
49
+ return this.globalMerkleMap.commitment();
50
+ }
51
+
52
+ private static createMerkleMap(map: ReadonlyMap<string, Buffer>): MerkleMap {
53
+ const sortedKeysStrings = [...map.keys()].sort();
54
+ const values = sortedKeysStrings.map((k) => {
55
+ const v = map.get(k);
56
+ if (!v) {
57
+ throw new Error('No value for key ' + k);
58
+ }
59
+ return v;
60
+ });
61
+ const sortedKeys = sortedKeysStrings.map((k) => Buffer.from(k, 'hex'));
62
+
63
+ return new MerkleMap(sortedKeys, values);
64
+ }
65
+ }
@@ -0,0 +1,136 @@
1
+ import CryptoJS from 'crypto-js';
2
+
3
+ // Helper function for SHA256 hashing (browser-compatible)
4
+ function sha256(data: Buffer): Buffer {
5
+ const wordArray = CryptoJS.lib.WordArray.create(data);
6
+ const hash = CryptoJS.SHA256(wordArray);
7
+ const hashBuffer = Buffer.from(hash.toString(CryptoJS.enc.Hex), 'hex');
8
+ return hashBuffer;
9
+ }
10
+
11
+ /**
12
+ * This class implements the merkle tree used by Ledger Bitcoin app v2+,
13
+ * which is documented at
14
+ * https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md
15
+ */
16
+ export class Merkle {
17
+ private leaves: Buffer[];
18
+ private rootNode: Node;
19
+ private leafNodes: Node[];
20
+ private h: (buf: Buffer) => Buffer;
21
+ constructor(leaves: Buffer[], hasher: (buf: Buffer) => Buffer = sha256) {
22
+ this.leaves = leaves;
23
+ this.h = hasher;
24
+ const nodes = this.calculateRoot(leaves);
25
+ this.rootNode = nodes.root;
26
+ this.leafNodes = nodes.leaves;
27
+ }
28
+ getRoot(): Buffer {
29
+ return this.rootNode.hash;
30
+ }
31
+ size(): number {
32
+ return this.leaves.length;
33
+ }
34
+ getLeaves(): Buffer[] {
35
+ return this.leaves;
36
+ }
37
+ getLeafHash(index: number): Buffer {
38
+ return this.leafNodes[index].hash;
39
+ }
40
+ getProof(index: number): Buffer[] {
41
+ if (index >= this.leaves.length) throw Error('Index out of bounds');
42
+ return proveNode(this.leafNodes[index]);
43
+ }
44
+
45
+ calculateRoot(leaves: Buffer[]): {
46
+ leaves: Node[];
47
+ root: Node;
48
+ } {
49
+ const n = leaves.length;
50
+ if (n == 0) {
51
+ return {
52
+ root: new Node(undefined, undefined, Buffer.alloc(32, 0)),
53
+ leaves: [],
54
+ };
55
+ }
56
+ if (n == 1) {
57
+ const newNode = new Node(undefined, undefined, leaves[0]);
58
+ return { root: newNode, leaves: [newNode] };
59
+ }
60
+ const leftCount = highestPowerOf2LessThan(n);
61
+ const leftBranch = this.calculateRoot(leaves.slice(0, leftCount));
62
+ const rightBranch = this.calculateRoot(leaves.slice(leftCount));
63
+ const leftChild = leftBranch.root;
64
+ const rightChild = rightBranch.root;
65
+ const hash = this.hashNode(leftChild.hash, rightChild.hash);
66
+ const node = new Node(leftChild, rightChild, hash);
67
+ leftChild.parent = node;
68
+ rightChild.parent = node;
69
+ return { root: node, leaves: leftBranch.leaves.concat(rightBranch.leaves) };
70
+ }
71
+
72
+ hashNode(left: Buffer, right: Buffer): Buffer {
73
+ return this.h(Buffer.concat([Buffer.from([1]), left, right]));
74
+ }
75
+ }
76
+
77
+ export function hashLeaf(
78
+ buf: Buffer,
79
+ hashFunction: (buf: Buffer) => Buffer = sha256
80
+ ): Buffer {
81
+ return hashConcat(Buffer.from([0]), buf, hashFunction);
82
+ }
83
+
84
+ function hashConcat(
85
+ bufA: Buffer,
86
+ bufB: Buffer,
87
+ hashFunction: (buf: Buffer) => Buffer
88
+ ): Buffer {
89
+ return hashFunction(Buffer.concat([bufA, bufB]));
90
+ }
91
+
92
+ class Node {
93
+ leftChild?: Node;
94
+ rightChild?: Node;
95
+ parent?: Node;
96
+ hash: Buffer;
97
+ constructor(left: Node | undefined, right: Node | undefined, hash: Buffer) {
98
+ this.leftChild = left;
99
+ this.rightChild = right;
100
+ this.hash = hash;
101
+ }
102
+ isLeaf(): boolean {
103
+ return this.leftChild == undefined;
104
+ }
105
+ }
106
+
107
+ function proveNode(node: Node): Buffer[] {
108
+ if (!node.parent) {
109
+ return [];
110
+ }
111
+ if (node.parent.leftChild == node) {
112
+ if (!node.parent.rightChild) {
113
+ throw new Error('Expected right child to exist');
114
+ }
115
+ return [node.parent.rightChild.hash, ...proveNode(node.parent)];
116
+ } else {
117
+ if (!node.parent.leftChild) {
118
+ throw new Error('Expected left child to exist');
119
+ }
120
+ return [node.parent.leftChild.hash, ...proveNode(node.parent)];
121
+ }
122
+ }
123
+
124
+ function highestPowerOf2LessThan(n: number) {
125
+ if (n < 2) {
126
+ throw Error('Expected n >= 2');
127
+ }
128
+ if (isPowerOf2(n)) {
129
+ return n / 2;
130
+ }
131
+ return 1 << Math.floor(Math.log2(n));
132
+ }
133
+
134
+ function isPowerOf2(n: number): boolean {
135
+ return (n & (n - 1)) == 0;
136
+ }
@@ -0,0 +1,49 @@
1
+ import { hashLeaf, Merkle } from './merkle';
2
+ import { createVarint } from './varint';
3
+
4
+ /**
5
+ * This implements "Merkelized Maps", documented at
6
+ * https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md#merkleized-maps
7
+ *
8
+ * A merkelized map consist of two merkle trees, one for the keys of
9
+ * a map and one for the values of the same map, thus the two merkle
10
+ * trees have the same shape. The commitment is the number elements
11
+ * in the map followed by the keys' merkle root followed by the
12
+ * values' merkle root.
13
+ */
14
+ export class MerkleMap {
15
+ readonly keys: readonly Buffer[];
16
+ readonly keysTree: Merkle;
17
+ readonly values: readonly Buffer[];
18
+ readonly valuesTree: Merkle;
19
+ /**
20
+ * @param keys Sorted list of (unhashed) keys
21
+ * @param values values, in corresponding order as the keys, and of equal length
22
+ */
23
+ constructor(keys: readonly Buffer[], values: readonly Buffer[]) {
24
+ if (keys.length != values.length) {
25
+ throw new Error('keys and values should have the same length');
26
+ }
27
+
28
+ // Sanity check: verify that keys are actually sorted and with no duplicates
29
+ for (let i = 0; i < keys.length - 1; i++) {
30
+ if (keys[i].toString('hex') >= keys[i + 1].toString('hex')) {
31
+ throw new Error('keys must be in strictly increasing order');
32
+ }
33
+ }
34
+
35
+ this.keys = keys;
36
+ this.keysTree = new Merkle(keys.map((k) => hashLeaf(k)));
37
+ this.values = values;
38
+ this.valuesTree = new Merkle(values.map((v) => hashLeaf(v)));
39
+ }
40
+
41
+ commitment(): Buffer {
42
+ // returns a buffer between 65 and 73 (included) bytes long
43
+ return Buffer.concat([
44
+ createVarint(this.keys.length),
45
+ this.keysTree.getRoot(),
46
+ this.valuesTree.getRoot(),
47
+ ]);
48
+ }
49
+ }
@@ -0,0 +1,91 @@
1
+ import CryptoJS from 'crypto-js';
2
+
3
+ // Helper function for SHA256 hashing (browser-compatible)
4
+ function sha256(data: Buffer): Buffer {
5
+ const wordArray = CryptoJS.lib.WordArray.create(data);
6
+ const hash = CryptoJS.SHA256(wordArray);
7
+ const hashBuffer = Buffer.from(hash.toString(CryptoJS.enc.Hex), 'hex');
8
+ return hashBuffer;
9
+ }
10
+
11
+ import { BufferWriter } from './buffertools';
12
+ import { hashLeaf, Merkle } from './merkle';
13
+
14
+ const WALLET_POLICY_V2 = 2;
15
+
16
+ /**
17
+ * The Bitcon hardware app uses a descriptors-like thing to describe
18
+ * how to construct output scripts from keys. A "Wallet Policy" consists
19
+ * of a "Descriptor Template" and a list of "keys". A key is basically
20
+ * a serialized BIP32 extended public key with some added derivation path
21
+ * information. This is documented at
22
+ * https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/wallet.md
23
+ */
24
+ export class WalletPolicy {
25
+ readonly name: string;
26
+ readonly descriptorTemplate: string;
27
+ readonly keys: readonly string[];
28
+ /**
29
+ * Creates and instance of a wallet policy.
30
+ * @param name an ascii string, up to 16 bytes long; it must be an empty string for default wallet policies
31
+ * @param descriptorTemplate the wallet policy template
32
+ * @param keys and array of the keys, with the key derivation information
33
+ */
34
+ constructor(
35
+ name: string,
36
+ descriptorTemplate: string,
37
+ keys: readonly string[]
38
+ ) {
39
+ this.name = name;
40
+ this.descriptorTemplate = descriptorTemplate;
41
+ this.keys = keys;
42
+ }
43
+
44
+ /**
45
+ * Returns the unique 32-bytes id of this wallet policy.
46
+ */
47
+ getId(): Buffer {
48
+ return sha256(this.serialize());
49
+ }
50
+
51
+ /**
52
+ * Serializes the wallet policy for transmission via the hardware wallet protocol.
53
+ * @returns the serialized wallet policy
54
+ */
55
+ serialize(): Buffer {
56
+ const keyBuffers = this.keys.map((k) => Buffer.from(k, 'ascii'));
57
+ const m = new Merkle(keyBuffers.map((k) => hashLeaf(k)));
58
+
59
+ const buf = new BufferWriter();
60
+ buf.writeUInt8(WALLET_POLICY_V2); // wallet version
61
+
62
+ // length of wallet name, and wallet name
63
+ buf.writeVarSlice(Buffer.from(this.name, 'ascii'));
64
+
65
+ // length of descriptor template
66
+ buf.writeVarInt(this.descriptorTemplate.length);
67
+ // sha256 hash of descriptor template
68
+ buf.writeSlice(sha256(Buffer.from(this.descriptorTemplate)));
69
+
70
+ // number of keys
71
+ buf.writeVarInt(this.keys.length);
72
+ // root of Merkle tree of keys
73
+ buf.writeSlice(m.getRoot());
74
+ return buf.buffer();
75
+ }
76
+ }
77
+
78
+ export type DefaultDescriptorTemplate =
79
+ | 'pkh(@0/**)'
80
+ | 'sh(wpkh(@0/**))'
81
+ | 'wpkh(@0/**)'
82
+ | 'tr(@0/**)';
83
+
84
+ /**
85
+ * Simplified class to handle default wallet policies that can be used without policy registration.
86
+ */
87
+ export class DefaultWalletPolicy extends WalletPolicy {
88
+ constructor(descriptorTemplate: DefaultDescriptorTemplate, key: string) {
89
+ super('', descriptorTemplate, [key]);
90
+ }
91
+ }