@ledgerhq/hw-app-btc 10.0.1 → 10.0.2-next.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.
- package/CHANGELOG.md +7 -0
- package/lib/Btc.d.ts.map +1 -1
- package/lib/Btc.js +72 -97
- package/lib/Btc.js.map +1 -1
- package/lib/BtcNew.js +217 -313
- package/lib/BtcNew.js.map +1 -1
- package/lib/BtcOld.js +46 -106
- package/lib/BtcOld.js.map +1 -1
- package/lib/bip32.js +12 -12
- package/lib/bip32.js.map +1 -1
- package/lib/buffertools.js +66 -69
- package/lib/buffertools.js.map +1 -1
- package/lib/compressPublicKey.js +3 -3
- package/lib/compressPublicKey.js.map +1 -1
- package/lib/constants.js +1 -1
- package/lib/createTransaction.d.ts +1 -1
- package/lib/createTransaction.d.ts.map +1 -1
- package/lib/createTransaction.js +285 -398
- package/lib/createTransaction.js.map +1 -1
- package/lib/debug.js +11 -13
- package/lib/debug.js.map +1 -1
- package/lib/finalizeInput.js +23 -62
- package/lib/finalizeInput.js.map +1 -1
- package/lib/getAppAndVersion.d.ts +1 -1
- package/lib/getAppAndVersion.d.ts.map +1 -1
- package/lib/getAppAndVersion.js +29 -72
- package/lib/getAppAndVersion.js.map +1 -1
- package/lib/getTrustedInput.js +108 -251
- package/lib/getTrustedInput.js.map +1 -1
- package/lib/getTrustedInputBIP143.js +9 -10
- package/lib/getTrustedInputBIP143.js.map +1 -1
- package/lib/getWalletPublicKey.d.ts +1 -1
- package/lib/getWalletPublicKey.d.ts.map +1 -1
- package/lib/getWalletPublicKey.js +27 -73
- package/lib/getWalletPublicKey.js.map +1 -1
- package/lib/hashPublicKey.js +4 -4
- package/lib/hashPublicKey.js.map +1 -1
- package/lib/index.js +3 -3
- package/lib/index.js.map +1 -1
- package/lib/newops/accounttype.d.ts +2 -2
- package/lib/newops/accounttype.d.ts.map +1 -1
- package/lib/newops/accounttype.js +85 -125
- package/lib/newops/accounttype.js.map +1 -1
- package/lib/newops/appClient.js +98 -205
- package/lib/newops/appClient.js.map +1 -1
- package/lib/newops/clientCommands.js +122 -213
- package/lib/newops/clientCommands.js.map +1 -1
- package/lib/newops/merkelizedPsbt.js +28 -75
- package/lib/newops/merkelizedPsbt.js.map +1 -1
- package/lib/newops/merkle.js +38 -67
- package/lib/newops/merkle.js.map +1 -1
- package/lib/newops/merkleMap.js +11 -12
- package/lib/newops/merkleMap.js.map +1 -1
- package/lib/newops/policy.d.ts +1 -1
- package/lib/newops/policy.d.ts.map +1 -1
- package/lib/newops/policy.js +17 -18
- package/lib/newops/policy.js.map +1 -1
- package/lib/newops/psbtExtractor.js +9 -9
- package/lib/newops/psbtExtractor.js.map +1 -1
- package/lib/newops/psbtFinalizer.js +22 -22
- package/lib/newops/psbtFinalizer.js.map +1 -1
- package/lib/newops/psbtv2.d.ts +1 -1
- package/lib/newops/psbtv2.d.ts.map +1 -1
- package/lib/newops/psbtv2.js +227 -286
- package/lib/newops/psbtv2.js.map +1 -1
- package/lib/serializeTransaction.js +13 -15
- package/lib/serializeTransaction.js.map +1 -1
- package/lib/shouldUseTrustedInputForSegwit.js +4 -5
- package/lib/shouldUseTrustedInputForSegwit.js.map +1 -1
- package/lib/signMessage.js +47 -99
- package/lib/signMessage.js.map +1 -1
- package/lib/signP2SHTransaction.d.ts +1 -1
- package/lib/signP2SHTransaction.d.ts.map +1 -1
- package/lib/signP2SHTransaction.js +91 -187
- package/lib/signP2SHTransaction.js.map +1 -1
- package/lib/signTransaction.js +8 -9
- package/lib/signTransaction.js.map +1 -1
- package/lib/splitTransaction.js +50 -54
- package/lib/splitTransaction.js.map +1 -1
- package/lib/startUntrustedHashTransactionInput.js +65 -167
- package/lib/startUntrustedHashTransactionInput.js.map +1 -1
- package/lib/types.js +1 -1
- package/lib/varint.js +10 -10
- package/lib/varint.js.map +1 -1
- package/lib-es/Btc.d.ts.map +1 -1
- package/lib-es/Btc.js +58 -84
- package/lib-es/Btc.js.map +1 -1
- package/lib-es/BtcNew.js +205 -302
- package/lib-es/BtcNew.js.map +1 -1
- package/lib-es/BtcOld.js +35 -96
- package/lib-es/BtcOld.js.map +1 -1
- package/lib-es/bip32.js +7 -7
- package/lib-es/bip32.js.map +1 -1
- package/lib-es/buffertools.js +62 -67
- package/lib-es/buffertools.js.map +1 -1
- package/lib-es/compressPublicKey.js +2 -2
- package/lib-es/compressPublicKey.js.map +1 -1
- package/lib-es/constants.js +12 -12
- package/lib-es/constants.js.map +1 -1
- package/lib-es/createTransaction.d.ts +1 -1
- package/lib-es/createTransaction.d.ts.map +1 -1
- package/lib-es/createTransaction.js +271 -384
- package/lib-es/createTransaction.js.map +1 -1
- package/lib-es/debug.js +10 -12
- package/lib-es/debug.js.map +1 -1
- package/lib-es/finalizeInput.js +20 -59
- package/lib-es/finalizeInput.js.map +1 -1
- package/lib-es/getAppAndVersion.d.ts +1 -1
- package/lib-es/getAppAndVersion.d.ts.map +1 -1
- package/lib-es/getAppAndVersion.js +27 -70
- package/lib-es/getAppAndVersion.js.map +1 -1
- package/lib-es/getTrustedInput.js +104 -247
- package/lib-es/getTrustedInput.js.map +1 -1
- package/lib-es/getTrustedInputBIP143.js +5 -6
- package/lib-es/getTrustedInputBIP143.js.map +1 -1
- package/lib-es/getWalletPublicKey.d.ts +1 -1
- package/lib-es/getWalletPublicKey.d.ts.map +1 -1
- package/lib-es/getWalletPublicKey.js +25 -71
- package/lib-es/getWalletPublicKey.js.map +1 -1
- package/lib-es/newops/accounttype.d.ts +2 -2
- package/lib-es/newops/accounttype.d.ts.map +1 -1
- package/lib-es/newops/accounttype.js +79 -123
- package/lib-es/newops/accounttype.js.map +1 -1
- package/lib-es/newops/appClient.js +92 -200
- package/lib-es/newops/appClient.js.map +1 -1
- package/lib-es/newops/clientCommands.js +117 -214
- package/lib-es/newops/clientCommands.js.map +1 -1
- package/lib-es/newops/merkelizedPsbt.js +25 -73
- package/lib-es/newops/merkelizedPsbt.js.map +1 -1
- package/lib-es/newops/merkle.js +36 -66
- package/lib-es/newops/merkle.js.map +1 -1
- package/lib-es/newops/merkleMap.js +8 -10
- package/lib-es/newops/merkleMap.js.map +1 -1
- package/lib-es/newops/policy.d.ts +1 -1
- package/lib-es/newops/policy.d.ts.map +1 -1
- package/lib-es/newops/policy.js +12 -14
- package/lib-es/newops/policy.js.map +1 -1
- package/lib-es/newops/psbtExtractor.js +7 -7
- package/lib-es/newops/psbtExtractor.js.map +1 -1
- package/lib-es/newops/psbtFinalizer.js +19 -19
- package/lib-es/newops/psbtFinalizer.js.map +1 -1
- package/lib-es/newops/psbtv2.d.ts +1 -1
- package/lib-es/newops/psbtv2.d.ts.map +1 -1
- package/lib-es/newops/psbtv2.js +225 -286
- package/lib-es/newops/psbtv2.js.map +1 -1
- package/lib-es/serializeTransaction.js +11 -13
- package/lib-es/serializeTransaction.js.map +1 -1
- package/lib-es/shouldUseTrustedInputForSegwit.js +1 -2
- package/lib-es/shouldUseTrustedInputForSegwit.js.map +1 -1
- package/lib-es/signMessage.js +44 -96
- package/lib-es/signMessage.js.map +1 -1
- package/lib-es/signP2SHTransaction.d.ts +1 -1
- package/lib-es/signP2SHTransaction.d.ts.map +1 -1
- package/lib-es/signP2SHTransaction.js +84 -180
- package/lib-es/signP2SHTransaction.js.map +1 -1
- package/lib-es/signTransaction.js +6 -7
- package/lib-es/signTransaction.js.map +1 -1
- package/lib-es/splitTransaction.js +46 -50
- package/lib-es/splitTransaction.js.map +1 -1
- package/lib-es/startUntrustedHashTransactionInput.js +62 -164
- package/lib-es/startUntrustedHashTransactionInput.js.map +1 -1
- package/lib-es/varint.js +9 -9
- package/lib-es/varint.js.map +1 -1
- package/package.json +5 -6
- package/src/Btc.ts +28 -5
package/lib-es/BtcNew.js
CHANGED
|
@@ -7,33 +7,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
-
function step(op) {
|
|
15
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
-
while (_) try {
|
|
17
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
-
switch (op[0]) {
|
|
20
|
-
case 0: case 1: t = op; break;
|
|
21
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
-
default:
|
|
25
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
-
if (t[2]) _.ops.pop();
|
|
30
|
-
_.trys.pop(); continue;
|
|
31
|
-
}
|
|
32
|
-
op = body.call(thisArg, _);
|
|
33
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
10
|
import { crypto } from "bitcoinjs-lib";
|
|
38
11
|
import { pointCompress } from "tiny-secp256k1";
|
|
39
12
|
import { getXpubComponents, hardenedPathOf, pathArrayToString, pathStringToArray, pubkeyFromXpub, } from "./bip32";
|
|
@@ -57,8 +30,8 @@ import { serializeTransaction } from "./serializeTransaction";
|
|
|
57
30
|
* be developed that exposes PSBT to the outer world, which would render
|
|
58
31
|
* a much cleaner implementation.
|
|
59
32
|
*/
|
|
60
|
-
|
|
61
|
-
|
|
33
|
+
export default class BtcNew {
|
|
34
|
+
constructor(client) {
|
|
62
35
|
this.client = client;
|
|
63
36
|
}
|
|
64
37
|
/**
|
|
@@ -91,26 +64,17 @@ var BtcNew = /** @class */ (function () {
|
|
|
91
64
|
*
|
|
92
65
|
* We opted for adding a new function, which can greatly simplify client code.
|
|
93
66
|
*/
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
case 1:
|
|
104
|
-
xpub = _b.sent();
|
|
105
|
-
xpubComponents = getXpubComponents(xpub);
|
|
106
|
-
if (xpubComponents.version != xpubVersion) {
|
|
107
|
-
throw new Error("Expected xpub version ".concat(xpubVersion, " doesn't match the xpub version from the device ").concat(xpubComponents.version));
|
|
108
|
-
}
|
|
109
|
-
return [2 /*return*/, xpub];
|
|
110
|
-
}
|
|
111
|
-
});
|
|
67
|
+
getWalletXpub({ path, xpubVersion, }) {
|
|
68
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
const pathElements = pathStringToArray(path);
|
|
70
|
+
const xpub = yield this.client.getExtendedPubkey(false, pathElements);
|
|
71
|
+
const xpubComponents = getXpubComponents(xpub);
|
|
72
|
+
if (xpubComponents.version != xpubVersion) {
|
|
73
|
+
throw new Error(`Expected xpub version ${xpubVersion} doesn't match the xpub version from the device ${xpubComponents.version}`);
|
|
74
|
+
}
|
|
75
|
+
return xpub;
|
|
112
76
|
});
|
|
113
|
-
}
|
|
77
|
+
}
|
|
114
78
|
/**
|
|
115
79
|
* This method returns a public key, a bitcoin address, and and a chaincode
|
|
116
80
|
* for a specific derivation path.
|
|
@@ -118,35 +82,25 @@ var BtcNew = /** @class */ (function () {
|
|
|
118
82
|
* Limitation: If the path is not a leaf node of a standard path, the address
|
|
119
83
|
* will be the empty string "", see this.getWalletAddress() for details.
|
|
120
84
|
*/
|
|
121
|
-
|
|
85
|
+
getWalletPublicKey(path, opts) {
|
|
122
86
|
var _a, _b;
|
|
123
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
address = _c.sent();
|
|
139
|
-
components = getXpubComponents(xpub);
|
|
140
|
-
uncompressedPubkey = Buffer.from(pointCompress(components.pubkey, false));
|
|
141
|
-
return [2 /*return*/, {
|
|
142
|
-
publicKey: uncompressedPubkey.toString("hex"),
|
|
143
|
-
bitcoinAddress: address,
|
|
144
|
-
chainCode: components.chaincode.toString("hex")
|
|
145
|
-
}];
|
|
146
|
-
}
|
|
147
|
-
});
|
|
87
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
88
|
+
if (!isPathNormal(path)) {
|
|
89
|
+
throw Error(`non-standard path: ${path}`);
|
|
90
|
+
}
|
|
91
|
+
const pathElements = pathStringToArray(path);
|
|
92
|
+
const xpub = yield this.client.getExtendedPubkey(false, pathElements);
|
|
93
|
+
const display = (_a = opts === null || opts === void 0 ? void 0 : opts.verify) !== null && _a !== void 0 ? _a : false;
|
|
94
|
+
const address = yield this.getWalletAddress(pathElements, descrTemplFrom((_b = opts === null || opts === void 0 ? void 0 : opts.format) !== null && _b !== void 0 ? _b : "legacy"), display);
|
|
95
|
+
const components = getXpubComponents(xpub);
|
|
96
|
+
const uncompressedPubkey = Buffer.from(pointCompress(components.pubkey, false));
|
|
97
|
+
return {
|
|
98
|
+
publicKey: uncompressedPubkey.toString("hex"),
|
|
99
|
+
bitcoinAddress: address,
|
|
100
|
+
chainCode: components.chaincode.toString("hex"),
|
|
101
|
+
};
|
|
148
102
|
});
|
|
149
|
-
}
|
|
103
|
+
}
|
|
150
104
|
/**
|
|
151
105
|
* Get an address for the specified path.
|
|
152
106
|
*
|
|
@@ -162,29 +116,19 @@ var BtcNew = /** @class */ (function () {
|
|
|
162
116
|
* way to get the address from the device. In this case we have to create it
|
|
163
117
|
* ourselves, but we don't at this time, and instead return an empty ("") address.
|
|
164
118
|
*/
|
|
165
|
-
|
|
166
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
case 1:
|
|
177
|
-
accountXpub = _a.sent();
|
|
178
|
-
return [4 /*yield*/, this.client.getMasterFingerprint()];
|
|
179
|
-
case 2:
|
|
180
|
-
masterFingerprint = _a.sent();
|
|
181
|
-
policy = new WalletPolicy(descrTempl, createKey(masterFingerprint, accountPath, accountXpub));
|
|
182
|
-
changeAndIndex = pathElements.slice(-2, pathElements.length);
|
|
183
|
-
return [2 /*return*/, this.client.getWalletAddress(policy, Buffer.alloc(32, 0), changeAndIndex[0], changeAndIndex[1], display)];
|
|
184
|
-
}
|
|
185
|
-
});
|
|
119
|
+
getWalletAddress(pathElements, descrTempl, display) {
|
|
120
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
121
|
+
const accountPath = hardenedPathOf(pathElements);
|
|
122
|
+
if (accountPath.length + 2 != pathElements.length) {
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
const accountXpub = yield this.client.getExtendedPubkey(false, accountPath);
|
|
126
|
+
const masterFingerprint = yield this.client.getMasterFingerprint();
|
|
127
|
+
const policy = new WalletPolicy(descrTempl, createKey(masterFingerprint, accountPath, accountXpub));
|
|
128
|
+
const changeAndIndex = pathElements.slice(-2, pathElements.length);
|
|
129
|
+
return this.client.getWalletAddress(policy, Buffer.alloc(32, 0), changeAndIndex[0], changeAndIndex[1], display);
|
|
186
130
|
});
|
|
187
|
-
}
|
|
131
|
+
}
|
|
188
132
|
/**
|
|
189
133
|
* Build and sign a transaction. See Btc.createPaymentTransaction for
|
|
190
134
|
* details on how to use this method.
|
|
@@ -193,111 +137,97 @@ var BtcNew = /** @class */ (function () {
|
|
|
193
137
|
* a psbt which is finally signed and finalized, and the extracted fully signed
|
|
194
138
|
* transaction is returned.
|
|
195
139
|
*/
|
|
196
|
-
|
|
197
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
progress();
|
|
235
|
-
pathElems = pathStringToArray(arg.associatedKeysets[i]);
|
|
236
|
-
if (!(accountXpub == "")) return [3 /*break*/, 4];
|
|
237
|
-
// We assume all inputs belong to the same account so we set
|
|
238
|
-
// the account xpub and path based on the first input.
|
|
239
|
-
accountPath = pathElems.slice(0, -2);
|
|
240
|
-
return [4 /*yield*/, this.client.getExtendedPubkey(false, accountPath)];
|
|
241
|
-
case 3:
|
|
242
|
-
accountXpub = _a.sent();
|
|
243
|
-
_a.label = 4;
|
|
244
|
-
case 4: return [4 /*yield*/, this.setInput(psbt, i, arg.inputs[i], pathElems, accountType, masterFp, arg.sigHashType)];
|
|
245
|
-
case 5:
|
|
246
|
-
_a.sent();
|
|
247
|
-
_a.label = 6;
|
|
248
|
-
case 6:
|
|
249
|
-
i++;
|
|
250
|
-
return [3 /*break*/, 2];
|
|
251
|
-
case 7:
|
|
252
|
-
outputsConcat = Buffer.from(arg.outputScriptHex, "hex");
|
|
253
|
-
outputsBufferReader = new BufferReader(outputsConcat);
|
|
254
|
-
outputCount = outputsBufferReader.readVarInt();
|
|
255
|
-
psbt.setGlobalOutputCount(outputCount);
|
|
256
|
-
return [4 /*yield*/, this.outputScriptAt(accountPath, accountType, arg.changePath)];
|
|
257
|
-
case 8:
|
|
258
|
-
changeData = _a.sent();
|
|
259
|
-
changeFound = !changeData;
|
|
260
|
-
for (i = 0; i < outputCount; i++) {
|
|
261
|
-
amount = Number(outputsBufferReader.readUInt64());
|
|
262
|
-
outputScript = outputsBufferReader.readVarSlice();
|
|
263
|
-
psbt.setOutputAmount(i, amount);
|
|
264
|
-
psbt.setOutputScript(i, outputScript);
|
|
265
|
-
isChange = changeData && outputScript.equals(changeData === null || changeData === void 0 ? void 0 : changeData.cond.scriptPubKey);
|
|
266
|
-
if (isChange) {
|
|
267
|
-
changeFound = true;
|
|
268
|
-
changePath = pathStringToArray(arg.changePath);
|
|
269
|
-
pubkey = changeData.pubkey;
|
|
270
|
-
accountType.setOwnOutput(i, changeData.cond, [pubkey], [changePath]);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (!changeFound) {
|
|
274
|
-
throw new Error("Change script not found among outputs! " +
|
|
275
|
-
(changeData === null || changeData === void 0 ? void 0 : changeData.cond.scriptPubKey.toString("hex")));
|
|
276
|
-
}
|
|
277
|
-
key = createKey(masterFp, accountPath, accountXpub);
|
|
278
|
-
p = new WalletPolicy(accountType.getDescriptorTemplate(), key);
|
|
279
|
-
// This is cheating, because it's not actually requested on the
|
|
280
|
-
// device yet, but it will be, soonish.
|
|
281
|
-
if (arg.onDeviceSignatureRequested)
|
|
282
|
-
arg.onDeviceSignatureRequested();
|
|
283
|
-
firstSigned = false;
|
|
284
|
-
progressCallback = function () {
|
|
285
|
-
if (!firstSigned) {
|
|
286
|
-
firstSigned = true;
|
|
287
|
-
arg.onDeviceSignatureGranted && arg.onDeviceSignatureGranted();
|
|
288
|
-
}
|
|
289
|
-
progress();
|
|
290
|
-
};
|
|
291
|
-
return [4 /*yield*/, this.signPsbt(psbt, p, progressCallback)];
|
|
292
|
-
case 9:
|
|
293
|
-
_a.sent();
|
|
294
|
-
finalize(psbt);
|
|
295
|
-
serializedTx = extract(psbt);
|
|
296
|
-
return [2 /*return*/, serializedTx.toString("hex")];
|
|
140
|
+
createPaymentTransaction(arg) {
|
|
141
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
const inputCount = arg.inputs.length;
|
|
143
|
+
if (inputCount == 0) {
|
|
144
|
+
throw Error("No inputs");
|
|
145
|
+
}
|
|
146
|
+
const psbt = new PsbtV2();
|
|
147
|
+
// The master fingerprint is needed when adding BIP32 derivation paths on
|
|
148
|
+
// the psbt.
|
|
149
|
+
const masterFp = yield this.client.getMasterFingerprint();
|
|
150
|
+
const accountType = accountTypeFromArg(arg, psbt, masterFp);
|
|
151
|
+
if (arg.lockTime != undefined) {
|
|
152
|
+
// The signer will assume locktime 0 if unset
|
|
153
|
+
psbt.setGlobalFallbackLocktime(arg.lockTime);
|
|
154
|
+
}
|
|
155
|
+
psbt.setGlobalInputCount(inputCount);
|
|
156
|
+
psbt.setGlobalPsbtVersion(2);
|
|
157
|
+
psbt.setGlobalTxVersion(2);
|
|
158
|
+
let notifyCount = 0;
|
|
159
|
+
const progress = () => {
|
|
160
|
+
if (!arg.onDeviceStreaming)
|
|
161
|
+
return;
|
|
162
|
+
arg.onDeviceStreaming({
|
|
163
|
+
total: 2 * inputCount,
|
|
164
|
+
index: notifyCount,
|
|
165
|
+
progress: ++notifyCount / (2 * inputCount),
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
let accountXpub = "";
|
|
169
|
+
let accountPath = [];
|
|
170
|
+
for (let i = 0; i < inputCount; i++) {
|
|
171
|
+
progress();
|
|
172
|
+
const pathElems = pathStringToArray(arg.associatedKeysets[i]);
|
|
173
|
+
if (accountXpub == "") {
|
|
174
|
+
// We assume all inputs belong to the same account so we set
|
|
175
|
+
// the account xpub and path based on the first input.
|
|
176
|
+
accountPath = pathElems.slice(0, -2);
|
|
177
|
+
accountXpub = yield this.client.getExtendedPubkey(false, accountPath);
|
|
297
178
|
}
|
|
298
|
-
|
|
179
|
+
yield this.setInput(psbt, i, arg.inputs[i], pathElems, accountType, masterFp, arg.sigHashType);
|
|
180
|
+
}
|
|
181
|
+
const outputsConcat = Buffer.from(arg.outputScriptHex, "hex");
|
|
182
|
+
const outputsBufferReader = new BufferReader(outputsConcat);
|
|
183
|
+
const outputCount = outputsBufferReader.readVarInt();
|
|
184
|
+
psbt.setGlobalOutputCount(outputCount);
|
|
185
|
+
const changeData = yield this.outputScriptAt(accountPath, accountType, arg.changePath);
|
|
186
|
+
// If the caller supplied a changePath, we must make sure there actually is
|
|
187
|
+
// a change output. If no change output found, we'll throw an error.
|
|
188
|
+
let changeFound = !changeData;
|
|
189
|
+
for (let i = 0; i < outputCount; i++) {
|
|
190
|
+
const amount = Number(outputsBufferReader.readUInt64());
|
|
191
|
+
const outputScript = outputsBufferReader.readVarSlice();
|
|
192
|
+
psbt.setOutputAmount(i, amount);
|
|
193
|
+
psbt.setOutputScript(i, outputScript);
|
|
194
|
+
// We won't know if we're paying to ourselves, because there's no
|
|
195
|
+
// information in arg to support multiple "change paths". One exception is
|
|
196
|
+
// if there are multiple outputs to the change address.
|
|
197
|
+
const isChange = changeData && outputScript.equals(changeData === null || changeData === void 0 ? void 0 : changeData.cond.scriptPubKey);
|
|
198
|
+
if (isChange) {
|
|
199
|
+
changeFound = true;
|
|
200
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
201
|
+
const changePath = pathStringToArray(arg.changePath);
|
|
202
|
+
const pubkey = changeData.pubkey;
|
|
203
|
+
accountType.setOwnOutput(i, changeData.cond, [pubkey], [changePath]);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (!changeFound) {
|
|
207
|
+
throw new Error("Change script not found among outputs! " +
|
|
208
|
+
(changeData === null || changeData === void 0 ? void 0 : changeData.cond.scriptPubKey.toString("hex")));
|
|
209
|
+
}
|
|
210
|
+
const key = createKey(masterFp, accountPath, accountXpub);
|
|
211
|
+
const p = new WalletPolicy(accountType.getDescriptorTemplate(), key);
|
|
212
|
+
// This is cheating, because it's not actually requested on the
|
|
213
|
+
// device yet, but it will be, soonish.
|
|
214
|
+
if (arg.onDeviceSignatureRequested)
|
|
215
|
+
arg.onDeviceSignatureRequested();
|
|
216
|
+
let firstSigned = false;
|
|
217
|
+
// This callback will be called once for each signature yielded.
|
|
218
|
+
const progressCallback = () => {
|
|
219
|
+
if (!firstSigned) {
|
|
220
|
+
firstSigned = true;
|
|
221
|
+
arg.onDeviceSignatureGranted && arg.onDeviceSignatureGranted();
|
|
222
|
+
}
|
|
223
|
+
progress();
|
|
224
|
+
};
|
|
225
|
+
yield this.signPsbt(psbt, p, progressCallback);
|
|
226
|
+
finalize(psbt);
|
|
227
|
+
const serializedTx = extract(psbt);
|
|
228
|
+
return serializedTx.toString("hex");
|
|
299
229
|
});
|
|
300
|
-
}
|
|
230
|
+
}
|
|
301
231
|
/**
|
|
302
232
|
* Calculates an output script along with public key and possible redeemScript
|
|
303
233
|
* from a path and accountType. The accountPath must be a prefix of path.
|
|
@@ -306,75 +236,60 @@ var BtcNew = /** @class */ (function () {
|
|
|
306
236
|
* wrapped p2wpkh), and pubkey at provided path. The values of these three
|
|
307
237
|
* properties depend on the accountType used.
|
|
308
238
|
*/
|
|
309
|
-
|
|
310
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
// going on.
|
|
320
|
-
for (i = 0; i < accountPath.length; i++) {
|
|
321
|
-
if (accountPath[i] != pathElems[i]) {
|
|
322
|
-
throw new Error("Path ".concat(path, " not in account ").concat(pathArrayToString(accountPath)));
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return [4 /*yield*/, this.client.getExtendedPubkey(false, pathElems)];
|
|
326
|
-
case 1:
|
|
327
|
-
xpub = _a.sent();
|
|
328
|
-
pubkey = pubkeyFromXpub(xpub);
|
|
329
|
-
cond = accountType.spendingCondition([pubkey]);
|
|
330
|
-
return [2 /*return*/, { cond: cond, pubkey: pubkey }];
|
|
239
|
+
outputScriptAt(accountPath, accountType, path) {
|
|
240
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
241
|
+
if (!path)
|
|
242
|
+
return undefined;
|
|
243
|
+
const pathElems = pathStringToArray(path);
|
|
244
|
+
// Make sure path is in our account, otherwise something fishy is probably
|
|
245
|
+
// going on.
|
|
246
|
+
for (let i = 0; i < accountPath.length; i++) {
|
|
247
|
+
if (accountPath[i] != pathElems[i]) {
|
|
248
|
+
throw new Error(`Path ${path} not in account ${pathArrayToString(accountPath)}`);
|
|
331
249
|
}
|
|
332
|
-
}
|
|
250
|
+
}
|
|
251
|
+
const xpub = yield this.client.getExtendedPubkey(false, pathElems);
|
|
252
|
+
const pubkey = pubkeyFromXpub(xpub);
|
|
253
|
+
const cond = accountType.spendingCondition([pubkey]);
|
|
254
|
+
return { cond, pubkey };
|
|
333
255
|
});
|
|
334
|
-
}
|
|
256
|
+
}
|
|
335
257
|
/**
|
|
336
258
|
* Adds relevant data about an input to the psbt. This includes sequence,
|
|
337
259
|
* previous txid, output index, spent UTXO, redeem script for wrapped p2wpkh,
|
|
338
260
|
* public key and its derivation path.
|
|
339
261
|
*/
|
|
340
|
-
|
|
341
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
spentOutput = { cond: spendCondition, amount: spentTxOutput.amount };
|
|
370
|
-
accountType.setInput(i, inputTxBuffer, spentOutput, [pubkey], [pathElements]);
|
|
371
|
-
psbt.setInputPreviousTxId(i, inputTxid);
|
|
372
|
-
psbt.setInputOutputIndex(i, spentOutputIndex);
|
|
373
|
-
return [2 /*return*/];
|
|
374
|
-
}
|
|
375
|
-
});
|
|
262
|
+
setInput(psbt, i, input, pathElements, accountType, masterFP, sigHashType) {
|
|
263
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
264
|
+
const inputTx = input[0];
|
|
265
|
+
const spentOutputIndex = input[1];
|
|
266
|
+
// redeemScript will be null for wrapped p2wpkh, we need to create it
|
|
267
|
+
// ourselves. But if set, it should be used.
|
|
268
|
+
const redeemScript = input[2] ? Buffer.from(input[2], "hex") : undefined;
|
|
269
|
+
const sequence = input[3];
|
|
270
|
+
if (sequence != undefined) {
|
|
271
|
+
psbt.setInputSequence(i, sequence);
|
|
272
|
+
}
|
|
273
|
+
if (sigHashType != undefined) {
|
|
274
|
+
psbt.setInputSighashType(i, sigHashType);
|
|
275
|
+
}
|
|
276
|
+
const inputTxBuffer = serializeTransaction(inputTx, true);
|
|
277
|
+
const inputTxid = crypto.hash256(inputTxBuffer);
|
|
278
|
+
const xpubBase58 = yield this.client.getExtendedPubkey(false, pathElements);
|
|
279
|
+
const pubkey = pubkeyFromXpub(xpubBase58);
|
|
280
|
+
if (!inputTx.outputs)
|
|
281
|
+
throw Error("Missing outputs array in transaction to sign");
|
|
282
|
+
const spentTxOutput = inputTx.outputs[spentOutputIndex];
|
|
283
|
+
const spendCondition = {
|
|
284
|
+
scriptPubKey: spentTxOutput.script,
|
|
285
|
+
redeemScript: redeemScript,
|
|
286
|
+
};
|
|
287
|
+
const spentOutput = { cond: spendCondition, amount: spentTxOutput.amount };
|
|
288
|
+
accountType.setInput(i, inputTxBuffer, spentOutput, [pubkey], [pathElements]);
|
|
289
|
+
psbt.setInputPreviousTxId(i, inputTxid);
|
|
290
|
+
psbt.setInputOutputIndex(i, spentOutputIndex);
|
|
376
291
|
});
|
|
377
|
-
}
|
|
292
|
+
}
|
|
378
293
|
/**
|
|
379
294
|
* This implements the "Signer" role of the BIP370 transaction signing
|
|
380
295
|
* process.
|
|
@@ -384,40 +299,30 @@ var BtcNew = /** @class */ (function () {
|
|
|
384
299
|
* comment in-line. The signatures returned from the hardware device is added
|
|
385
300
|
* to the appropriate input fields of the PSBT.
|
|
386
301
|
*/
|
|
387
|
-
|
|
388
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
psbt.setInputTapKeySig(k, v);
|
|
407
|
-
}
|
|
408
|
-
else {
|
|
409
|
-
pubkey = pubkeys[0];
|
|
410
|
-
psbt.setInputPartialSig(k, pubkey, v);
|
|
411
|
-
}
|
|
412
|
-
});
|
|
413
|
-
return [2 /*return*/];
|
|
302
|
+
signPsbt(psbt, walletPolicy, progressCallback) {
|
|
303
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
304
|
+
const sigs = yield this.client.signPsbt(psbt, walletPolicy, Buffer.alloc(32, 0), progressCallback);
|
|
305
|
+
sigs.forEach((v, k) => {
|
|
306
|
+
// Note: Looking at BIP32 derivation does not work in the generic case,
|
|
307
|
+
// since some inputs might not have a BIP32-derived pubkey.
|
|
308
|
+
const pubkeys = psbt.getInputKeyDatas(k, psbtIn.BIP32_DERIVATION);
|
|
309
|
+
let pubkey;
|
|
310
|
+
if (pubkeys.length != 1) {
|
|
311
|
+
// No legacy BIP32_DERIVATION, assume we're using taproot.
|
|
312
|
+
pubkey = psbt.getInputKeyDatas(k, psbtIn.TAP_BIP32_DERIVATION);
|
|
313
|
+
if (pubkey.length == 0) {
|
|
314
|
+
throw Error(`Missing pubkey derivation for input ${k}`);
|
|
315
|
+
}
|
|
316
|
+
psbt.setInputTapKeySig(k, v);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
pubkey = pubkeys[0];
|
|
320
|
+
psbt.setInputPartialSig(k, pubkey, v);
|
|
414
321
|
}
|
|
415
322
|
});
|
|
416
323
|
});
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
}());
|
|
420
|
-
export default BtcNew;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
421
326
|
function descrTemplFrom(addressFormat) {
|
|
422
327
|
if (addressFormat == "legacy")
|
|
423
328
|
return "pkh(@0)";
|
|
@@ -459,17 +364,15 @@ function accountTypeFromArg(arg, psbt, masterFp) {
|
|
|
459
364
|
*/
|
|
460
365
|
function isPathNormal(path) {
|
|
461
366
|
//path is not deepest hardened node of a standard path or deeper, use BtcOld
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
return n === undefined || n === 0 || n === 1;
|
|
468
|
-
};
|
|
367
|
+
const h = 0x80000000; //HARDENED from bip32
|
|
368
|
+
const pathElems = pathStringToArray(path);
|
|
369
|
+
const hard = (n) => n >= h;
|
|
370
|
+
const soft = (n) => n === undefined || n < h;
|
|
371
|
+
const change = (n) => n === undefined || n === 0 || n === 1;
|
|
469
372
|
if (pathElems.length >= 3 &&
|
|
470
373
|
pathElems.length <= 5 &&
|
|
471
|
-
[44 + h, 49 + h, 84 + h, 86 + h].some(
|
|
472
|
-
[0 + h, 1 + h].some(
|
|
374
|
+
[44 + h, 49 + h, 84 + h, 86 + h].some((v) => v == pathElems[0]) &&
|
|
375
|
+
[0 + h, 1 + h].some((v) => v == pathElems[1]) &&
|
|
473
376
|
hard(pathElems[2]) &&
|
|
474
377
|
change(pathElems[3]) &&
|
|
475
378
|
soft(pathElems[4])) {
|
|
@@ -478,7 +381,7 @@ function isPathNormal(path) {
|
|
|
478
381
|
if (pathElems.length >= 4 &&
|
|
479
382
|
pathElems.length <= 6 &&
|
|
480
383
|
48 + h == pathElems[0] &&
|
|
481
|
-
[0 + h, 1 + h].some(
|
|
384
|
+
[0 + h, 1 + h].some((v) => v == pathElems[1]) &&
|
|
482
385
|
hard(pathElems[2]) &&
|
|
483
386
|
hard(pathElems[3]) &&
|
|
484
387
|
change(pathElems[4]) &&
|