@ledgerhq/hw-app-btc 6.2.0 → 6.9.1-6.9.1-taproot.0.0

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 (187) hide show
  1. package/lib/Btc.d.ts +7 -3
  2. package/lib/Btc.d.ts.map +1 -1
  3. package/lib/Btc.js +99 -30
  4. package/lib/Btc.js.map +1 -1
  5. package/lib/BtcNew.d.ts +70 -0
  6. package/lib/BtcNew.d.ts.map +1 -0
  7. package/lib/BtcNew.js +372 -0
  8. package/lib/BtcNew.js.map +1 -0
  9. package/lib/BtcOld.d.ts +114 -0
  10. package/lib/BtcOld.d.ts.map +1 -0
  11. package/lib/BtcOld.js +138 -0
  12. package/lib/BtcOld.js.map +1 -0
  13. package/lib/bip32.d.ts +8 -0
  14. package/lib/bip32.d.ts.map +1 -1
  15. package/lib/bip32.js +32 -3
  16. package/lib/bip32.js.map +1 -1
  17. package/lib/buffertools.d.ts +28 -0
  18. package/lib/buffertools.d.ts.map +1 -0
  19. package/lib/buffertools.js +100 -0
  20. package/lib/buffertools.js.map +1 -0
  21. package/lib/createTransaction.d.ts.map +1 -1
  22. package/lib/createTransaction.js +16 -16
  23. package/lib/createTransaction.js.map +1 -1
  24. package/lib/finalizeInput.js +1 -1
  25. package/lib/finalizeInput.js.map +1 -1
  26. package/lib/getAppAndVersion.js +1 -1
  27. package/lib/getAppAndVersion.js.map +1 -1
  28. package/lib/getTrustedInput.js +6 -6
  29. package/lib/getTrustedInput.js.map +1 -1
  30. package/lib/getTrustedInputBIP143.js +2 -2
  31. package/lib/getTrustedInputBIP143.js.map +1 -1
  32. package/lib/getWalletPublicKey.d.ts +1 -1
  33. package/lib/getWalletPublicKey.d.ts.map +1 -1
  34. package/lib/getWalletPublicKey.js +1 -1
  35. package/lib/getWalletPublicKey.js.map +1 -1
  36. package/lib/hashPublicKey.d.ts +1 -1
  37. package/lib/hashPublicKey.d.ts.map +1 -1
  38. package/lib/hashPublicKey.js +1 -1
  39. package/lib/hashPublicKey.js.map +1 -1
  40. package/lib/index.d.ts +3 -0
  41. package/lib/index.d.ts.map +1 -0
  42. package/lib/index.js +8 -0
  43. package/lib/index.js.map +1 -0
  44. package/lib/newops/appClient.d.ts +14 -0
  45. package/lib/newops/appClient.d.ts.map +1 -0
  46. package/lib/newops/appClient.js +242 -0
  47. package/lib/newops/appClient.js.map +1 -0
  48. package/lib/newops/clientCommands.d.ts +61 -0
  49. package/lib/newops/clientCommands.d.ts.map +1 -0
  50. package/lib/newops/clientCommands.js +331 -0
  51. package/lib/newops/clientCommands.js.map +1 -0
  52. package/lib/newops/merkelizedPsbt.d.ts +15 -0
  53. package/lib/newops/merkelizedPsbt.d.ts.map +1 -0
  54. package/lib/newops/merkelizedPsbt.js +91 -0
  55. package/lib/newops/merkelizedPsbt.js.map +1 -0
  56. package/lib/newops/merkle.d.ts +29 -0
  57. package/lib/newops/merkle.d.ts.map +1 -0
  58. package/lib/newops/merkle.js +133 -0
  59. package/lib/newops/merkle.js.map +1 -0
  60. package/lib/newops/merkleMap.d.ts +15 -0
  61. package/lib/newops/merkleMap.d.ts.map +1 -0
  62. package/lib/newops/merkleMap.js +37 -0
  63. package/lib/newops/merkleMap.js.map +1 -0
  64. package/lib/newops/policy.d.ts +14 -0
  65. package/lib/newops/policy.d.ts.map +1 -0
  66. package/lib/newops/policy.js +40 -0
  67. package/lib/newops/policy.js.map +1 -0
  68. package/lib/newops/psbtExtractor.d.ts +4 -0
  69. package/lib/newops/psbtExtractor.d.ts.map +1 -0
  70. package/lib/newops/psbtExtractor.js +36 -0
  71. package/lib/newops/psbtExtractor.js.map +1 -0
  72. package/lib/newops/psbtFinalizer.d.ts +7 -0
  73. package/lib/newops/psbtFinalizer.d.ts.map +1 -0
  74. package/lib/newops/psbtFinalizer.js +111 -0
  75. package/lib/newops/psbtFinalizer.js.map +1 -0
  76. package/lib/newops/psbtv2.d.ts +129 -0
  77. package/lib/newops/psbtv2.d.ts.map +1 -0
  78. package/lib/newops/psbtv2.js +478 -0
  79. package/lib/newops/psbtv2.js.map +1 -0
  80. package/lib/serializeTransaction.js +4 -4
  81. package/lib/serializeTransaction.js.map +1 -1
  82. package/lib/signP2SHTransaction.js +5 -5
  83. package/lib/signP2SHTransaction.js.map +1 -1
  84. package/lib/signTransaction.js +1 -1
  85. package/lib/signTransaction.js.map +1 -1
  86. package/lib/splitTransaction.js +7 -7
  87. package/lib/splitTransaction.js.map +1 -1
  88. package/lib/startUntrustedHashTransactionInput.js +2 -2
  89. package/lib/startUntrustedHashTransactionInput.js.map +1 -1
  90. package/lib/varint.d.ts.map +1 -1
  91. package/lib/varint.js +1 -0
  92. package/lib/varint.js.map +1 -1
  93. package/lib-es/Btc.d.ts +7 -3
  94. package/lib-es/Btc.d.ts.map +1 -1
  95. package/lib-es/Btc.js +92 -26
  96. package/lib-es/Btc.js.map +1 -1
  97. package/lib-es/BtcNew.d.ts +70 -0
  98. package/lib-es/BtcNew.d.ts.map +1 -0
  99. package/lib-es/BtcNew.js +370 -0
  100. package/lib-es/BtcNew.js.map +1 -0
  101. package/lib-es/BtcOld.d.ts +114 -0
  102. package/lib-es/BtcOld.d.ts.map +1 -0
  103. package/lib-es/BtcOld.js +136 -0
  104. package/lib-es/BtcOld.js.map +1 -0
  105. package/lib-es/bip32.d.ts +8 -0
  106. package/lib-es/bip32.d.ts.map +1 -1
  107. package/lib-es/bip32.js +26 -2
  108. package/lib-es/bip32.js.map +1 -1
  109. package/lib-es/buffertools.d.ts +28 -0
  110. package/lib-es/buffertools.d.ts.map +1 -0
  111. package/lib-es/buffertools.js +94 -0
  112. package/lib-es/buffertools.js.map +1 -0
  113. package/lib-es/createTransaction.d.ts.map +1 -1
  114. package/lib-es/getWalletPublicKey.d.ts +1 -1
  115. package/lib-es/getWalletPublicKey.d.ts.map +1 -1
  116. package/lib-es/getWalletPublicKey.js.map +1 -1
  117. package/lib-es/hashPublicKey.d.ts +1 -1
  118. package/lib-es/hashPublicKey.d.ts.map +1 -1
  119. package/lib-es/index.d.ts +3 -0
  120. package/lib-es/index.d.ts.map +1 -0
  121. package/lib-es/index.js +3 -0
  122. package/lib-es/index.js.map +1 -0
  123. package/lib-es/newops/appClient.d.ts +14 -0
  124. package/lib-es/newops/appClient.d.ts.map +1 -0
  125. package/lib-es/newops/appClient.js +239 -0
  126. package/lib-es/newops/appClient.js.map +1 -0
  127. package/lib-es/newops/clientCommands.d.ts +61 -0
  128. package/lib-es/newops/clientCommands.d.ts.map +1 -0
  129. package/lib-es/newops/clientCommands.js +328 -0
  130. package/lib-es/newops/clientCommands.js.map +1 -0
  131. package/lib-es/newops/merkelizedPsbt.d.ts +15 -0
  132. package/lib-es/newops/merkelizedPsbt.d.ts.map +1 -0
  133. package/lib-es/newops/merkelizedPsbt.js +88 -0
  134. package/lib-es/newops/merkelizedPsbt.js.map +1 -0
  135. package/lib-es/newops/merkle.d.ts +29 -0
  136. package/lib-es/newops/merkle.d.ts.map +1 -0
  137. package/lib-es/newops/merkle.js +129 -0
  138. package/lib-es/newops/merkle.js.map +1 -0
  139. package/lib-es/newops/merkleMap.d.ts +15 -0
  140. package/lib-es/newops/merkleMap.d.ts.map +1 -0
  141. package/lib-es/newops/merkleMap.js +34 -0
  142. package/lib-es/newops/merkleMap.js.map +1 -0
  143. package/lib-es/newops/policy.d.ts +14 -0
  144. package/lib-es/newops/policy.d.ts.map +1 -0
  145. package/lib-es/newops/policy.js +36 -0
  146. package/lib-es/newops/policy.js.map +1 -0
  147. package/lib-es/newops/psbtExtractor.d.ts +4 -0
  148. package/lib-es/newops/psbtExtractor.d.ts.map +1 -0
  149. package/lib-es/newops/psbtExtractor.js +32 -0
  150. package/lib-es/newops/psbtExtractor.js.map +1 -0
  151. package/lib-es/newops/psbtFinalizer.d.ts +7 -0
  152. package/lib-es/newops/psbtFinalizer.d.ts.map +1 -0
  153. package/lib-es/newops/psbtFinalizer.js +107 -0
  154. package/lib-es/newops/psbtFinalizer.js.map +1 -0
  155. package/lib-es/newops/psbtv2.d.ts +129 -0
  156. package/lib-es/newops/psbtv2.d.ts.map +1 -0
  157. package/lib-es/newops/psbtv2.js +475 -0
  158. package/lib-es/newops/psbtv2.js.map +1 -0
  159. package/lib-es/varint.d.ts.map +1 -1
  160. package/lib-es/varint.js +1 -0
  161. package/lib-es/varint.js.map +1 -1
  162. package/package.json +7 -4
  163. package/src/Btc.ts +42 -25
  164. package/src/BtcNew.ts +326 -0
  165. package/src/BtcOld.ts +156 -0
  166. package/src/bip32.ts +34 -2
  167. package/src/buffertools.ts +102 -0
  168. package/src/createTransaction.ts +2 -2
  169. package/src/getWalletPublicKey.ts +6 -1
  170. package/src/hashPublicKey.ts +1 -1
  171. package/src/index.ts +2 -0
  172. package/src/newops/appClient.ts +178 -0
  173. package/src/newops/clientCommands.ts +312 -0
  174. package/src/newops/merkelizedPsbt.ts +55 -0
  175. package/src/newops/merkle.ts +123 -0
  176. package/src/newops/merkleMap.ts +39 -0
  177. package/src/newops/policy.ts +52 -0
  178. package/src/newops/psbtExtractor.ts +33 -0
  179. package/src/newops/psbtFinalizer.ts +110 -0
  180. package/src/newops/psbtv2.ts +548 -0
  181. package/src/varint.ts +2 -0
  182. package/tests/Btc.integration.test.ts +89 -0
  183. package/tests/Btc.test.ts +6 -0
  184. package/tests/newops/BtcNew.test.ts +646 -0
  185. package/tests/newops/common.ts +25 -0
  186. package/tests/newops/merkle.test.ts +97 -0
  187. package/tests/trustedInputs.test.ts +4 -0
@@ -0,0 +1,548 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
3
+ import { BufferReader, BufferWriter } from "../buffertools";
4
+
5
+ export enum psbtGlobal {
6
+ TX_VERSION = 0x02,
7
+ FALLBACK_LOCKTIME = 0x03,
8
+ INPUT_COUNT = 0x04,
9
+ OUTPUT_COUNT = 0x05,
10
+ TX_MODIFIABLE = 0x06,
11
+ VERSION = 0xfb,
12
+ }
13
+ export enum psbtIn {
14
+ NON_WITNESS_UTXO = 0x00,
15
+ WITNESS_UTXO = 0x01,
16
+ PARTIAL_SIG = 0x02,
17
+ REDEEM_SCRIPT = 0x04,
18
+ BIP32_DERIVATION = 0x06,
19
+ FINAL_SCRIPTSIG = 0x07,
20
+ FINAL_SCRIPTWITNESS = 0x08,
21
+ PREVIOUS_TXID = 0x0e,
22
+ OUTPUT_INDEX = 0x0f,
23
+ SEQUENCE = 0x10,
24
+ TAP_KEY_SIG = 0x13,
25
+ TAP_BIP32_DERIVATION = 0x16,
26
+ }
27
+ export enum psbtOut {
28
+ REDEEM_SCRIPT = 0x00,
29
+ BIP_32_DERIVATION = 0x02,
30
+ AMOUNT = 0x03,
31
+ SCRIPT = 0x04,
32
+ TAP_BIP32_DERIVATION = 0x07,
33
+ }
34
+
35
+ const PSBT_MAGIC_BYTES = Buffer.of(0x70, 0x73, 0x62, 0x74, 0xff);
36
+
37
+ export class NoSuchEntry extends Error {}
38
+
39
+ export class PsbtV2 {
40
+ protected globalMap: Map<string, Buffer> = new Map();
41
+ protected inputMaps: Map<string, Buffer>[] = [];
42
+ protected outputMaps: Map<string, Buffer>[] = [];
43
+
44
+ setGlobalTxVersion(version: number) {
45
+ this.setGlobal(psbtGlobal.TX_VERSION, uint32LE(version));
46
+ }
47
+ getGlobalTxVersion(): number {
48
+ return this.getGlobal(psbtGlobal.TX_VERSION).readUInt32LE(0);
49
+ }
50
+ setGlobalFallbackLocktime(locktime: number) {
51
+ this.setGlobal(psbtGlobal.FALLBACK_LOCKTIME, uint32LE(locktime));
52
+ }
53
+ getGlobalFallbackLocktime(): number | undefined {
54
+ return this.getGlobalOptional(psbtGlobal.FALLBACK_LOCKTIME)?.readUInt32LE(
55
+ 0
56
+ );
57
+ }
58
+ setGlobalInputCount(inputCount: number) {
59
+ this.setGlobal(psbtGlobal.INPUT_COUNT, varint(inputCount));
60
+ }
61
+ getGlobalInputCount(): number {
62
+ return fromVarint(this.getGlobal(psbtGlobal.INPUT_COUNT));
63
+ }
64
+ setGlobalOutputCount(outputCount: number) {
65
+ this.setGlobal(psbtGlobal.OUTPUT_COUNT, varint(outputCount));
66
+ }
67
+ getGlobalOutputCount(): number {
68
+ return fromVarint(this.getGlobal(psbtGlobal.OUTPUT_COUNT));
69
+ }
70
+ setGlobalTxModifiable(byte: Buffer) {
71
+ this.setGlobal(psbtGlobal.TX_MODIFIABLE, byte);
72
+ }
73
+ getGlobalTxModifiable(): Buffer | undefined {
74
+ return this.getGlobalOptional(psbtGlobal.TX_MODIFIABLE);
75
+ }
76
+ setGlobalPsbtVersion(psbtVersion: number) {
77
+ this.setGlobal(psbtGlobal.VERSION, uint32LE(psbtVersion));
78
+ }
79
+ getGlobalPsbtVersion(): number {
80
+ return this.getGlobal(psbtGlobal.VERSION).readUInt32LE(0);
81
+ }
82
+
83
+ setInputNonWitnessUtxo(inputIndex: number, transaction: Buffer) {
84
+ this.setInput(inputIndex, psbtIn.NON_WITNESS_UTXO, b(), transaction);
85
+ }
86
+ getInputNonWitnessUtxo(inputIndex: number): Buffer | undefined {
87
+ return this.getInputOptional(inputIndex, psbtIn.NON_WITNESS_UTXO, b());
88
+ }
89
+ setInputWitnessUtxo(
90
+ inputIndex: number,
91
+ amount: Buffer,
92
+ scriptPubKey: Buffer
93
+ ) {
94
+ const buf = new BufferWriter();
95
+ buf.writeSlice(amount);
96
+ buf.writeVarSlice(scriptPubKey);
97
+ this.setInput(inputIndex, psbtIn.WITNESS_UTXO, b(), buf.buffer());
98
+ }
99
+ getInputWitnessUtxo(
100
+ inputIndex: number
101
+ ): { amount: Buffer; scriptPubKey: Buffer } | undefined {
102
+ const utxo = this.getInputOptional(inputIndex, psbtIn.WITNESS_UTXO, b());
103
+ if (!utxo) return undefined;
104
+ const buf = new BufferReader(utxo);
105
+ return { amount: buf.readSlice(8), scriptPubKey: buf.readVarSlice() };
106
+ }
107
+ setInputPartialSig(inputIndex: number, pubkey: Buffer, signature: Buffer) {
108
+ this.setInput(inputIndex, psbtIn.PARTIAL_SIG, pubkey, signature);
109
+ }
110
+ getInputPartialSig(inputIndex: number, pubkey: Buffer): Buffer | undefined {
111
+ return this.getInputOptional(inputIndex, psbtIn.PARTIAL_SIG, pubkey);
112
+ }
113
+ setInputRedeemScript(inputIndex: number, redeemScript: Buffer) {
114
+ this.setInput(inputIndex, psbtIn.REDEEM_SCRIPT, b(), redeemScript);
115
+ }
116
+ getInputRedeemScript(inputIndex: number): Buffer | undefined {
117
+ return this.getInputOptional(inputIndex, psbtIn.REDEEM_SCRIPT, b());
118
+ }
119
+ setInputBip32Derivation(
120
+ inputIndex: number,
121
+ pubkey: Buffer,
122
+ masterFingerprint: Buffer,
123
+ path: number[]
124
+ ) {
125
+ this.setInput(
126
+ inputIndex,
127
+ psbtIn.BIP32_DERIVATION,
128
+ pubkey,
129
+ this.encodeBip32Derivation(masterFingerprint, path)
130
+ );
131
+ }
132
+ getInputBip32Derivation(
133
+ inputIndex: number,
134
+ pubkey: Buffer
135
+ ): { masterFingerprint: Buffer; path: number[] } | undefined {
136
+ const buf = this.getInputOptional(
137
+ inputIndex,
138
+ psbtIn.BIP32_DERIVATION,
139
+ pubkey
140
+ );
141
+ if (!buf) return undefined;
142
+ return this.decodeBip32Derivation(buf);
143
+ }
144
+ setInputFinalScriptsig(inputIndex: number, scriptSig: Buffer) {
145
+ this.setInput(inputIndex, psbtIn.FINAL_SCRIPTSIG, b(), scriptSig);
146
+ }
147
+ getInputFinalScriptsig(inputIndex: number): Buffer | undefined {
148
+ return this.getInputOptional(inputIndex, psbtIn.FINAL_SCRIPTSIG, b());
149
+ }
150
+ setInputFinalScriptwitness(inputIndex: number, scriptWitness: Buffer) {
151
+ this.setInput(inputIndex, psbtIn.FINAL_SCRIPTWITNESS, b(), scriptWitness);
152
+ }
153
+ getInputFinalScriptwitness(inputIndex: number): Buffer {
154
+ return this.getInput(inputIndex, psbtIn.FINAL_SCRIPTWITNESS, b());
155
+ }
156
+ setInputPreviousTxId(inputIndex: number, txid: Buffer) {
157
+ this.setInput(inputIndex, psbtIn.PREVIOUS_TXID, b(), txid);
158
+ }
159
+ getInputPreviousTxid(inputIndex: number): Buffer {
160
+ return this.getInput(inputIndex, psbtIn.PREVIOUS_TXID, b());
161
+ }
162
+ setInputOutputIndex(inputIndex: number, outputIndex: number) {
163
+ this.setInput(inputIndex, psbtIn.OUTPUT_INDEX, b(), uint32LE(outputIndex));
164
+ }
165
+ getInputOutputIndex(inputIndex: number): number {
166
+ return this.getInput(inputIndex, psbtIn.OUTPUT_INDEX, b()).readUInt32LE(0);
167
+ }
168
+ setInputSequence(inputIndex: number, sequence: number) {
169
+ this.setInput(inputIndex, psbtIn.SEQUENCE, b(), uint32LE(sequence));
170
+ }
171
+ getInputSequence(inputIndex: number): number {
172
+ return (
173
+ this.getInputOptional(inputIndex, psbtIn.SEQUENCE, b())?.readUInt32LE(
174
+ 0
175
+ ) ?? 0xffffffff
176
+ );
177
+ }
178
+ setInputTapKeySig(inputIndex: number, sig: Buffer) {
179
+ this.setInput(inputIndex, psbtIn.TAP_KEY_SIG, b(), sig);
180
+ }
181
+ getInputTapKeySig(inputIndex: number): Buffer | undefined {
182
+ return this.getInputOptional(inputIndex, psbtIn.TAP_KEY_SIG, b());
183
+ }
184
+ setInputTapBip32Derivation(
185
+ inputIndex: number,
186
+ pubkey: Buffer,
187
+ hashes: Buffer[],
188
+ masterFingerprint: Buffer,
189
+ path: number[]
190
+ ) {
191
+ const buf = this.encodeTapBip32Derivation(hashes, masterFingerprint, path);
192
+ this.setInput(inputIndex, psbtIn.TAP_BIP32_DERIVATION, pubkey, buf);
193
+ }
194
+ getInputTapBip32Derivation(
195
+ inputIndex: number,
196
+ pubkey: Buffer
197
+ ): { hashes: Buffer[]; masterFingerprint: Buffer; path: number[] } {
198
+ const buf = this.getInput(inputIndex, psbtIn.TAP_BIP32_DERIVATION, pubkey);
199
+ return this.decodeTapBip32Derivation(buf);
200
+ }
201
+ getInputKeyDatas(inputIndex: number, keyType: KeyType): Buffer[] {
202
+ return this.getKeyDatas(this.inputMaps[inputIndex], keyType);
203
+ }
204
+
205
+ setOutputRedeemScript(outputIndex: number, redeemScript: Buffer) {
206
+ this.setOutput(outputIndex, psbtOut.REDEEM_SCRIPT, b(), redeemScript);
207
+ }
208
+ getOutputRedeemScript(outputIndex: number): Buffer {
209
+ return this.getOutput(outputIndex, psbtOut.REDEEM_SCRIPT, b());
210
+ }
211
+ setOutputBip32Derivation(
212
+ outputIndex: number,
213
+ pubkey: Buffer,
214
+ masterFingerprint: Buffer,
215
+ path: number[]
216
+ ) {
217
+ this.setOutput(
218
+ outputIndex,
219
+ psbtOut.BIP_32_DERIVATION,
220
+ pubkey,
221
+ this.encodeBip32Derivation(masterFingerprint, path)
222
+ );
223
+ }
224
+ getOutputBip32Derivation(
225
+ outputIndex: number,
226
+ pubkey: Buffer
227
+ ): { masterFingerprint: Buffer; path: number[] } {
228
+ const buf = this.getOutput(outputIndex, psbtOut.BIP_32_DERIVATION, pubkey);
229
+ return this.decodeBip32Derivation(buf);
230
+ }
231
+ setOutputAmount(outputIndex: number, amount: number) {
232
+ this.setOutput(outputIndex, psbtOut.AMOUNT, b(), uint64LE(amount));
233
+ }
234
+ getOutputAmount(outputIndex: number): number {
235
+ return Number(
236
+ this.getOutput(outputIndex, psbtOut.AMOUNT, b()).readBigUInt64LE(0)
237
+ );
238
+ }
239
+ setOutputScript(outputIndex: number, scriptPubKey: Buffer) {
240
+ this.setOutput(outputIndex, psbtOut.SCRIPT, b(), scriptPubKey);
241
+ }
242
+ getOutputScript(outputIndex: number): Buffer {
243
+ return this.getOutput(outputIndex, psbtOut.SCRIPT, b());
244
+ }
245
+ setOutputTapBip32Derivation(
246
+ outputIndex: number,
247
+ pubkey: Buffer,
248
+ hashes: Buffer[],
249
+ fingerprint: Buffer,
250
+ path: number[]
251
+ ) {
252
+ const buf = this.encodeTapBip32Derivation(hashes, fingerprint, path);
253
+ this.setOutput(outputIndex, psbtOut.TAP_BIP32_DERIVATION, pubkey, buf);
254
+ }
255
+ getOutputTapBip32Derivation(
256
+ outputIndex: number,
257
+ pubkey: Buffer
258
+ ): { hashes: Buffer[]; masterFingerprint: Buffer; path: number[] } {
259
+ const buf = this.getOutput(
260
+ outputIndex,
261
+ psbtOut.TAP_BIP32_DERIVATION,
262
+ pubkey
263
+ );
264
+ return this.decodeTapBip32Derivation(buf);
265
+ }
266
+
267
+ deleteInputEntries(inputIndex: number, keyTypes: psbtIn[]) {
268
+ const map = this.inputMaps[inputIndex];
269
+ map.forEach((_v, k, m) => {
270
+ if (this.isKeyType(k, keyTypes)) {
271
+ m.delete(k);
272
+ }
273
+ });
274
+ }
275
+
276
+ copy(to: PsbtV2) {
277
+ this.copyMap(this.globalMap, to.globalMap);
278
+ this.copyMaps(this.inputMaps, to.inputMaps);
279
+ this.copyMaps(this.outputMaps, to.outputMaps);
280
+ }
281
+ copyMaps(from: Map<string, Buffer>[], to: Map<string, Buffer>[]) {
282
+ from.forEach((m, index) => {
283
+ const to_index = new Map();
284
+ this.copyMap(m, to_index);
285
+ to[index] = to_index;
286
+ });
287
+ }
288
+ copyMap(from: Map<string, Buffer>, to: Map<string, Buffer>) {
289
+ from.forEach((v, k) => to.set(k, Buffer.from(v)));
290
+ }
291
+ serialize(): Buffer {
292
+ const buf = new BufferWriter();
293
+ buf.writeSlice(Buffer.of(0x70, 0x73, 0x62, 0x74, 0xff));
294
+ serializeMap(buf, this.globalMap);
295
+ this.inputMaps.forEach((map) => {
296
+ serializeMap(buf, map);
297
+ });
298
+ this.outputMaps.forEach((map) => {
299
+ serializeMap(buf, map);
300
+ });
301
+ return buf.buffer();
302
+ }
303
+ deserialize(psbt: Buffer) {
304
+ const buf = new BufferReader(psbt);
305
+ if (!buf.readSlice(5).equals(PSBT_MAGIC_BYTES)) {
306
+ throw new Error("Invalid magic bytes");
307
+ }
308
+ while (this.readKeyPair(this.globalMap, buf));
309
+ for (let i = 0; i < this.getGlobalInputCount(); i++) {
310
+ this.inputMaps[i] = new Map();
311
+ while (this.readKeyPair(this.inputMaps[i], buf));
312
+ }
313
+ for (let i = 0; i < this.getGlobalOutputCount(); i++) {
314
+ this.outputMaps[i] = new Map();
315
+ while (this.readKeyPair(this.outputMaps[i], buf));
316
+ }
317
+ }
318
+ private readKeyPair(map: Map<string, Buffer>, buf: BufferReader): boolean {
319
+ const keyLen = buf.readVarInt();
320
+ if (keyLen == 0) {
321
+ return false;
322
+ }
323
+ const keyType = buf.readUInt8();
324
+ const keyData = buf.readSlice(keyLen - 1);
325
+ const value = buf.readVarSlice();
326
+ set(map, keyType, keyData, value);
327
+ return true;
328
+ }
329
+ private getKeyDatas(map: Map<string, Buffer>, keyType: KeyType): Buffer[] {
330
+ const result: Buffer[] = [];
331
+ map.forEach((_v, k) => {
332
+ if (this.isKeyType(k, [keyType])) {
333
+ result.push(Buffer.from(k.substring(2), "hex"));
334
+ }
335
+ });
336
+ return result;
337
+ }
338
+ private isKeyType(hexKey: string, keyTypes: KeyType[]): boolean {
339
+ const keyType = Buffer.from(hexKey.substring(0, 2), "hex").readUInt8(0);
340
+ return keyTypes.some((k) => k == keyType);
341
+ }
342
+ private setGlobal(keyType: KeyType, value: Buffer) {
343
+ const key = new Key(keyType, Buffer.of());
344
+ this.globalMap.set(key.toString(), value);
345
+ }
346
+ private getGlobal(keyType: KeyType): Buffer {
347
+ return get(this.globalMap, keyType, b(), false)!;
348
+ }
349
+ private getGlobalOptional(keyType: KeyType): Buffer | undefined {
350
+ return get(this.globalMap, keyType, b(), true);
351
+ }
352
+ private setInput(
353
+ index: number,
354
+ keyType: KeyType,
355
+ keyData: Buffer,
356
+ value: Buffer
357
+ ) {
358
+ set(this.getMap(index, this.inputMaps), keyType, keyData, value);
359
+ }
360
+ private getMap(
361
+ index: number,
362
+ maps: Map<string, Buffer>[]
363
+ ): Map<string, Buffer> {
364
+ if (maps[index]) {
365
+ return maps[index];
366
+ }
367
+ return (maps[index] = new Map());
368
+ }
369
+ private getInput(index: number, keyType: KeyType, keyData: Buffer): Buffer {
370
+ return get(this.inputMaps[index], keyType, keyData, false)!;
371
+ }
372
+ private getInputOptional(
373
+ index: number,
374
+ keyType: KeyType,
375
+ keyData: Buffer
376
+ ): Buffer | undefined {
377
+ return get(this.inputMaps[index], keyType, keyData, true);
378
+ }
379
+ private setOutput(
380
+ index: number,
381
+ keyType: KeyType,
382
+ keyData: Buffer,
383
+ value: Buffer
384
+ ) {
385
+ set(this.getMap(index, this.outputMaps), keyType, keyData, value);
386
+ }
387
+ private getOutput(index: number, keyType: KeyType, keyData: Buffer): Buffer {
388
+ return get(this.outputMaps[index], keyType, keyData, false)!;
389
+ }
390
+ private getOutputOptional(
391
+ index: number,
392
+ keyType: KeyType,
393
+ keyData: Buffer
394
+ ): Buffer | undefined {
395
+ return get(this.outputMaps[index], keyType, keyData, true);
396
+ }
397
+ private encodeBip32Derivation(masterFingerprint: Buffer, path: number[]) {
398
+ const buf = new BufferWriter();
399
+ this.writeBip32Derivation(buf, masterFingerprint, path);
400
+ return buf.buffer();
401
+ }
402
+ private decodeBip32Derivation(
403
+ buffer: Buffer
404
+ ): { masterFingerprint: Buffer; path: number[] } {
405
+ const buf = new BufferReader(buffer);
406
+ return this.readBip32Derivation(buf);
407
+ }
408
+ private writeBip32Derivation(
409
+ buf: BufferWriter,
410
+ masterFingerprint: Buffer,
411
+ path: number[]
412
+ ) {
413
+ buf.writeSlice(masterFingerprint);
414
+ path.forEach((element) => {
415
+ buf.writeUInt32(element);
416
+ });
417
+ }
418
+ private readBip32Derivation(
419
+ buf: BufferReader
420
+ ): { masterFingerprint: Buffer; path: number[] } {
421
+ const masterFingerprint = buf.readSlice(4);
422
+ const path: number[] = [];
423
+ while (buf.offset < buf.buffer.length) {
424
+ path.push(buf.readUInt32());
425
+ }
426
+ return { masterFingerprint, path };
427
+ }
428
+ private encodeTapBip32Derivation(
429
+ hashes: Buffer[],
430
+ masterFingerprint: Buffer,
431
+ path: number[]
432
+ ): Buffer {
433
+ const buf = new BufferWriter();
434
+ buf.writeVarInt(hashes.length);
435
+ hashes.forEach((h) => {
436
+ buf.writeSlice(h);
437
+ });
438
+ this.writeBip32Derivation(buf, masterFingerprint, path);
439
+ return buf.buffer();
440
+ }
441
+ private decodeTapBip32Derivation(
442
+ buffer: Buffer
443
+ ): { hashes: Buffer[]; masterFingerprint: Buffer; path: number[] } {
444
+ const buf = new BufferReader(buffer);
445
+ const hashCount = buf.readVarInt();
446
+ const hashes: Buffer[] = [];
447
+ for (let i = 0; i < hashCount; i++) {
448
+ hashes.push(buf.readSlice(32));
449
+ }
450
+ const deriv = this.readBip32Derivation(buf);
451
+ return { hashes, ...deriv };
452
+ }
453
+ }
454
+ function get(
455
+ map: Map<string, Buffer>,
456
+ keyType: KeyType,
457
+ keyData: Buffer,
458
+ acceptUndefined: boolean
459
+ ): Buffer | undefined {
460
+ if (!map) throw Error("No such map");
461
+ const key = new Key(keyType, keyData);
462
+ const value = map.get(key.toString());
463
+ if (!value) {
464
+ if (acceptUndefined) {
465
+ return undefined;
466
+ }
467
+ throw new NoSuchEntry(key.toString());
468
+ }
469
+ // Make sure to return a copy, to protect the underlying data.
470
+ return Buffer.from(value);
471
+ }
472
+ type KeyType = number;
473
+
474
+ class Key {
475
+ keyType: KeyType;
476
+ keyData: Buffer;
477
+ constructor(keyType: KeyType, keyData: Buffer) {
478
+ this.keyType = keyType;
479
+ this.keyData = keyData;
480
+ }
481
+ toString(): string {
482
+ const buf = new BufferWriter();
483
+ this.toBuffer(buf);
484
+ return buf.buffer().toString("hex");
485
+ }
486
+ serialize(buf: BufferWriter) {
487
+ buf.writeVarInt(1 + this.keyData.length);
488
+ this.toBuffer(buf);
489
+ }
490
+ private toBuffer(buf: BufferWriter) {
491
+ buf.writeUInt8(this.keyType);
492
+ buf.writeSlice(this.keyData);
493
+ }
494
+ }
495
+ class KeyPair {
496
+ key: Key;
497
+ value: Buffer;
498
+ constructor(key: Key, value: Buffer) {
499
+ this.key = key;
500
+ this.value = value;
501
+ }
502
+ serialize(buf: BufferWriter) {
503
+ this.key.serialize(buf);
504
+ buf.writeVarSlice(this.value);
505
+ }
506
+ }
507
+ function createKey(buf: Buffer): Key {
508
+ return new Key(buf.readUInt8(0), buf.slice(1));
509
+ }
510
+ function serializeMap(buf: BufferWriter, map: Map<string, Buffer>) {
511
+ for (const k in map.keys) {
512
+ const value = map.get(k)!;
513
+ const keyPair = new KeyPair(createKey(Buffer.from(k, "hex")), value);
514
+ keyPair.serialize(buf);
515
+ }
516
+ buf.writeUInt8(0);
517
+ }
518
+
519
+ function b(): Buffer {
520
+ return Buffer.of();
521
+ }
522
+ function set(
523
+ map: Map<string, Buffer>,
524
+ keyType: KeyType,
525
+ keyData: Buffer,
526
+ value: Buffer
527
+ ) {
528
+ const key = new Key(keyType, keyData);
529
+ map.set(key.toString(), value);
530
+ }
531
+ function uint32LE(n: number): Buffer {
532
+ const b = Buffer.alloc(4);
533
+ b.writeUInt32LE(n, 0);
534
+ return b;
535
+ }
536
+ function uint64LE(n: number): Buffer {
537
+ const b = Buffer.alloc(8);
538
+ b.writeBigUInt64LE(BigInt(n), 0);
539
+ return b;
540
+ }
541
+ function varint(n: number): Buffer {
542
+ const b = new BufferWriter();
543
+ b.writeVarInt(n);
544
+ return b.buffer();
545
+ }
546
+ function fromVarint(buf: Buffer): number {
547
+ return new BufferReader(buf).readVarInt();
548
+ }
package/src/varint.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // TODO: this does not support 64-bit varints!
2
+
1
3
  export function getVarint(data: Buffer, offset: number): [number, number] {
2
4
  if (data[offset] < 0xfd) {
3
5
  return [data[offset], 1];
@@ -0,0 +1,89 @@
1
+ // import Transport from "@ledgerhq/hw-transport-node-hid";
2
+ import SpeculosTransport from "@ledgerhq/hw-transport-node-speculos";
3
+ import BtcNew from "../src/BtcNew";
4
+ import Btc from "../src/Btc";
5
+ import { AppClient } from "../src/newops/appClient";
6
+ import { getXpubComponents } from "../src/bip32";
7
+ import { compressPublicKey } from "../src/compressPublicKey";
8
+ import Transport from "@ledgerhq/hw-transport";
9
+
10
+ const xpubs = {
11
+ "m/44'/1'/0'":
12
+ "tpubDCwYjpDhUdPGP5rS3wgNg13mTrrjBuG8V9VpWbyptX6TRPbNoZVXsoVUSkCjmQ8jJycjuDKBb9eataSymXakTTaGifxR6kmVsfFehH1ZgJT",
13
+ "m/44'/1'/10'":
14
+ "tpubDCwYjpDhUdPGp21gSpVay2QPJVh6WNySWMXPhbcu1DsxH31dF7mY18oibbu5RxCLBc1Szerjscuc3D5HyvfYqfRvc9mesewnFqGmPjney4d",
15
+ "m/44'/1'/2'/1/42":
16
+ "tpubDGF9YgHKv6qh777rcqVhpmDrbNzgophJM9ec7nHiSfrbss7fVBXoqhmZfohmJSvhNakDHAspPHjVVNL657tLbmTXvSeGev2vj5kzjMaeupT",
17
+ "m/48'/1'/4'/1'/0/7":
18
+ "tpubDK8WPFx4WJo1R9mEL7Wq325wBiXvkAe8ipgb9Q1QBDTDUD2YeCfutWtzY88NPokZqJyRPKHLGwTNLT7jBG59aC6VH8q47LDGQitPB6tX2d7",
19
+ "m/49'/1'/1'/1/3":
20
+ "tpubDGnetmJDCL18TyaaoyRAYbkSE9wbHktSdTS4mfsR6inC8c2r6TjdBt3wkqEQhHYPtXpa46xpxDaCXU2PRNUGVvDzAHPG6hHRavYbwAGfnFr",
21
+ "m/84'/1'/2'/0/10":
22
+ "tpubDG9YpSUwScWJBBSrhnAT47NcT4NZGLcY18cpkaiWHnkUCi19EtCh8Heeox268NaFF6o56nVeSXuTyK6jpzTvV1h68Kr3edA8AZp27MiLUNt",
23
+ "m/86'/1'/4'/1/12":
24
+ "tpubDHTZ815MvTaRmo6Qg1rnU6TEU4ZkWyA56jA1UgpmMcBGomnSsyo34EZLoctzZY9MTJ6j7bhccceUeXZZLxZj5vgkVMYfcZ7DNPsyRdFpS3f",
25
+ };
26
+ /*
27
+ const addresses = {
28
+ "m/44'/1'/0'/0/0": "mz5vLWdM1wHVGSmXUkhKVvZbJ2g4epMXSm",
29
+ "m/84'/1'/0'/0/0": "tb1qzdr7s2sr0dwmkwx033r4nujzk86u0cy6fmzfjk",
30
+ "m/49'/1'/0'/0/0": "2MyHkbusvLomaarGYMqyq7q9pSBYJRwWcsw",
31
+ };
32
+ */
33
+ test("Basic happypath BtcOld test", async () => {
34
+ await runGetWalletPublicKey("old");
35
+ });
36
+
37
+ test("Basic happypath BtcNew test", async () => {
38
+ await runGetWalletPublicKey("new");
39
+ });
40
+
41
+ async function transport() {
42
+ return await SpeculosTransport.open({ apduPort: 9999 });
43
+ }
44
+
45
+ async function impl(
46
+ variant: "old" | "new",
47
+ transport: Transport
48
+ ): Promise<Btc | BtcNew> {
49
+ if (variant === "old") {
50
+ return new Btc(transport);
51
+ }
52
+ const client = new AppClient(transport);
53
+ const btc = new BtcNew(client);
54
+ return btc;
55
+ }
56
+
57
+ async function runGetWalletPublicKey(variant: "old" | "new") {
58
+ let tr;
59
+ try {
60
+ tr = await transport();
61
+ } catch (e) {
62
+ console.error("FIXME: SPECULOS TEST IGNORED BECAUSE INSTANCE IS NOT UP", e);
63
+ return;
64
+ }
65
+ try {
66
+ const btc = await impl(variant, tr);
67
+
68
+ const account = await btc.getWalletPublicKey("m/44'/1'/0'");
69
+ const expectedAccount = getXpubComponents(xpubs["m/44'/1'/0'"]);
70
+
71
+ const uncompressesPubkey =
72
+ "04e84c7f4b7662faed9f5eb2d812d9b7bcf0e0bf2a33b17ac45e38b8459669c321816e33dd5541f01ac4e9dbee1ad25fccf2bdc1dc16336b3ae66463c093d612ed";
73
+ expect(compressPublicKey(Buffer.from(uncompressesPubkey, "hex"))).toEqual(
74
+ expectedAccount.pubkey
75
+ );
76
+
77
+ expect(account.chainCode).toEqual(
78
+ expectedAccount.chaincode.toString("hex")
79
+ );
80
+ expect(account.publicKey).toEqual(uncompressesPubkey);
81
+
82
+ const keydata = await btc.getWalletPublicKey("m/44'/1'/0'/0/0");
83
+ expect(keydata.bitcoinAddress).toEqual(
84
+ "mz5vLWdM1wHVGSmXUkhKVvZbJ2g4epMXSm"
85
+ );
86
+ } finally {
87
+ tr.close();
88
+ }
89
+ }
package/tests/Btc.test.ts CHANGED
@@ -7,6 +7,8 @@ import Btc from "../src/Btc";
7
7
  test("btc.getWalletPublicKey", async () => {
8
8
  const transport = await openTransportReplayer(
9
9
  RecordStore.fromString(`
10
+ => b001000000
11
+ <= 0107426974636f696e06312e332e323301029000
10
12
  => e040000011048000002c800000008000000000000000
11
13
  <= 410486b865b52b753d0a84d09bc20063fab5d8453ec33c215d4019a5801c9c6438b917770b2782e29a9ecc6edb67cd1f0fbf05ec4c1236884b6d686d6be3b1588abb2231334b453654666641724c683466564d36756f517a7673597135767765744a63564dbce80dd580792cd18af542790e56aa813178dc28644bb5f03dbd44c85f2d2e7a9000
12
14
  `)
@@ -27,6 +29,8 @@ test("btc 2", async () => {
27
29
  RecordStore.fromString(`
28
30
  => b001000000
29
31
  <= 0107426974636f696e06312e332e323301029000
32
+ => b001000000
33
+ <= 0107426974636f696e06312e332e323301029000
30
34
  => e042000009000000010100000001
31
35
  <= 9000
32
36
  => e0428000254ea60aeac5252c14291d428915bd7ccd1bfc4af009f4d4dc57ae597ed0420b71010000008a
@@ -160,6 +164,8 @@ test("btc seg multi", async () => {
160
164
  const transport = await openTransportReplayer(
161
165
  RecordStore.fromString(`
162
166
  => b001000000
167
+ <= 0107426974636f696e06312e332e323301029000
168
+ => b001000000
163
169
  <= 0107426974636f696e06312e332e323201029000
164
170
  => e040000015058000003180000001800000050000000000000000
165
171
  <= 4104f004370a593b3cde1511801a1151c86dd09a2f246a3f9ac3ef0b0240c0aeb506feddb0a785f5039c3e3e829db9692364e333256284d0fe312177cb12b88551162131764a4336523431416334685a61704a7863334c5a6e69334e7169445837514562141c248b44b74cbe35a3a92801cfebaf895df8d65f5830264097260c863fc1e59000