1 | 'use strict';
|
2 | Object.defineProperty(exports, '__esModule', { value: true });
|
3 | const baddress = require('./address');
|
4 | const bufferutils_1 = require('./bufferutils');
|
5 | const classify = require('./classify');
|
6 | const bcrypto = require('./crypto');
|
7 | const ECPair = require('./ecpair');
|
8 | const networks = require('./networks');
|
9 | const payments = require('./payments');
|
10 | const bscript = require('./script');
|
11 | const script_1 = require('./script');
|
12 | const transaction_1 = require('./transaction');
|
13 | const types = require('./types');
|
14 | const typeforce = require('typeforce');
|
15 | const SCRIPT_TYPES = classify.types;
|
16 | const PREVOUT_TYPES = new Set([
|
17 |
|
18 | 'p2pkh',
|
19 | 'p2pk',
|
20 | 'p2wpkh',
|
21 | 'p2ms',
|
22 |
|
23 | 'p2sh-p2pkh',
|
24 | 'p2sh-p2pk',
|
25 | 'p2sh-p2wpkh',
|
26 | 'p2sh-p2ms',
|
27 |
|
28 | 'p2wsh-p2pkh',
|
29 | 'p2wsh-p2pk',
|
30 | 'p2wsh-p2ms',
|
31 |
|
32 | 'p2sh-p2wsh-p2pkh',
|
33 | 'p2sh-p2wsh-p2pk',
|
34 | 'p2sh-p2wsh-p2ms',
|
35 | ]);
|
36 | function tfMessage(type, value, message) {
|
37 | try {
|
38 | typeforce(type, value);
|
39 | } catch (err) {
|
40 | throw new Error(message);
|
41 | }
|
42 | }
|
43 | function txIsString(tx) {
|
44 | return typeof tx === 'string' || tx instanceof String;
|
45 | }
|
46 | function txIsTransaction(tx) {
|
47 | return tx instanceof transaction_1.Transaction;
|
48 | }
|
49 | class TransactionBuilder {
|
50 |
|
51 |
|
52 | constructor(network = networks.bitcoin, maximumFeeRate = 2500) {
|
53 | this.network = network;
|
54 | this.maximumFeeRate = maximumFeeRate;
|
55 | this.__PREV_TX_SET = {};
|
56 | this.__INPUTS = [];
|
57 | this.__TX = new transaction_1.Transaction();
|
58 | this.__TX.version = 2;
|
59 | this.__USE_LOW_R = false;
|
60 | console.warn(
|
61 | 'Deprecation Warning: TransactionBuilder will be removed in the future. ' +
|
62 | '(v6.x.x or later) Please use the Psbt class instead. Examples of usage ' +
|
63 | 'are available in the transactions-psbt.js integration test file on our ' +
|
64 | 'Github. A high level explanation is available in the psbt.ts and psbt.js ' +
|
65 | 'files as well.',
|
66 | );
|
67 | }
|
68 | static fromTransaction(transaction, network) {
|
69 | const txb = new TransactionBuilder(network);
|
70 |
|
71 | txb.setVersion(transaction.version);
|
72 | txb.setLockTime(transaction.locktime);
|
73 |
|
74 | transaction.outs.forEach(txOut => {
|
75 | txb.addOutput(txOut.script, txOut.value);
|
76 | });
|
77 |
|
78 | transaction.ins.forEach(txIn => {
|
79 | txb.__addInputUnsafe(txIn.hash, txIn.index, {
|
80 | sequence: txIn.sequence,
|
81 | script: txIn.script,
|
82 | witness: txIn.witness,
|
83 | });
|
84 | });
|
85 |
|
86 | txb.__INPUTS.forEach((input, i) => {
|
87 | fixMultisigOrder(input, transaction, i);
|
88 | });
|
89 | return txb;
|
90 | }
|
91 | setLowR(setting) {
|
92 | typeforce(typeforce.maybe(typeforce.Boolean), setting);
|
93 | if (setting === undefined) {
|
94 | setting = true;
|
95 | }
|
96 | this.__USE_LOW_R = setting;
|
97 | return setting;
|
98 | }
|
99 | setLockTime(locktime) {
|
100 | typeforce(types.UInt32, locktime);
|
101 |
|
102 | if (
|
103 | this.__INPUTS.some(input => {
|
104 | if (!input.signatures) return false;
|
105 | return input.signatures.some(s => s !== undefined);
|
106 | })
|
107 | ) {
|
108 | throw new Error('No, this would invalidate signatures');
|
109 | }
|
110 | this.__TX.locktime = locktime;
|
111 | }
|
112 | setVersion(version) {
|
113 | typeforce(types.UInt32, version);
|
114 |
|
115 | this.__TX.version = version;
|
116 | }
|
117 | addInput(txHash, vout, sequence, prevOutScript) {
|
118 | if (!this.__canModifyInputs()) {
|
119 | throw new Error('No, this would invalidate signatures');
|
120 | }
|
121 | let value;
|
122 |
|
123 | if (txIsString(txHash)) {
|
124 |
|
125 | txHash = bufferutils_1.reverseBuffer(Buffer.from(txHash, 'hex'));
|
126 |
|
127 | } else if (txIsTransaction(txHash)) {
|
128 | const txOut = txHash.outs[vout];
|
129 | prevOutScript = txOut.script;
|
130 | value = txOut.value;
|
131 | txHash = txHash.getHash(false);
|
132 | }
|
133 | return this.__addInputUnsafe(txHash, vout, {
|
134 | sequence,
|
135 | prevOutScript,
|
136 | value,
|
137 | });
|
138 | }
|
139 | addOutput(scriptPubKey, value) {
|
140 | if (!this.__canModifyOutputs()) {
|
141 | throw new Error('No, this would invalidate signatures');
|
142 | }
|
143 |
|
144 | if (typeof scriptPubKey === 'string') {
|
145 | scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network);
|
146 | }
|
147 | return this.__TX.addOutput(scriptPubKey, value);
|
148 | }
|
149 | build() {
|
150 | return this.__build(false);
|
151 | }
|
152 | buildIncomplete() {
|
153 | return this.__build(true);
|
154 | }
|
155 | sign(
|
156 | signParams,
|
157 | keyPair,
|
158 | redeemScript,
|
159 | hashType,
|
160 | witnessValue,
|
161 | witnessScript,
|
162 | ) {
|
163 | trySign(
|
164 | getSigningData(
|
165 | this.network,
|
166 | this.__INPUTS,
|
167 | this.__needsOutputs.bind(this),
|
168 | this.__TX,
|
169 | signParams,
|
170 | keyPair,
|
171 | redeemScript,
|
172 | hashType,
|
173 | witnessValue,
|
174 | witnessScript,
|
175 | this.__USE_LOW_R,
|
176 | ),
|
177 | );
|
178 | }
|
179 | __addInputUnsafe(txHash, vout, options) {
|
180 | if (transaction_1.Transaction.isCoinbaseHash(txHash)) {
|
181 | throw new Error('coinbase inputs not supported');
|
182 | }
|
183 | const prevTxOut = txHash.toString('hex') + ':' + vout;
|
184 | if (this.__PREV_TX_SET[prevTxOut] !== undefined)
|
185 | throw new Error('Duplicate TxOut: ' + prevTxOut);
|
186 | let input = {};
|
187 |
|
188 | if (options.script !== undefined) {
|
189 | input = expandInput(options.script, options.witness || []);
|
190 | }
|
191 |
|
192 | if (options.value !== undefined) {
|
193 | input.value = options.value;
|
194 | }
|
195 |
|
196 | if (!input.prevOutScript && options.prevOutScript) {
|
197 | let prevOutType;
|
198 | if (!input.pubkeys && !input.signatures) {
|
199 | const expanded = expandOutput(options.prevOutScript);
|
200 | if (expanded.pubkeys) {
|
201 | input.pubkeys = expanded.pubkeys;
|
202 | input.signatures = expanded.signatures;
|
203 | }
|
204 | prevOutType = expanded.type;
|
205 | }
|
206 | input.prevOutScript = options.prevOutScript;
|
207 | input.prevOutType = prevOutType || classify.output(options.prevOutScript);
|
208 | }
|
209 | const vin = this.__TX.addInput(
|
210 | txHash,
|
211 | vout,
|
212 | options.sequence,
|
213 | options.scriptSig,
|
214 | );
|
215 | this.__INPUTS[vin] = input;
|
216 | this.__PREV_TX_SET[prevTxOut] = true;
|
217 | return vin;
|
218 | }
|
219 | __build(allowIncomplete) {
|
220 | if (!allowIncomplete) {
|
221 | if (!this.__TX.ins.length) throw new Error('Transaction has no inputs');
|
222 | if (!this.__TX.outs.length) throw new Error('Transaction has no outputs');
|
223 | }
|
224 | const tx = this.__TX.clone();
|
225 |
|
226 | this.__INPUTS.forEach((input, i) => {
|
227 | if (!input.prevOutType && !allowIncomplete)
|
228 | throw new Error('Transaction is not complete');
|
229 | const result = build(input.prevOutType, input, allowIncomplete);
|
230 | if (!result) {
|
231 | if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD)
|
232 | throw new Error('Unknown input type');
|
233 | if (!allowIncomplete) throw new Error('Not enough information');
|
234 | return;
|
235 | }
|
236 | tx.setInputScript(i, result.input);
|
237 | tx.setWitness(i, result.witness);
|
238 | });
|
239 | if (!allowIncomplete) {
|
240 |
|
241 | if (this.__overMaximumFees(tx.virtualSize())) {
|
242 | throw new Error('Transaction has absurd fees');
|
243 | }
|
244 | }
|
245 | return tx;
|
246 | }
|
247 | __canModifyInputs() {
|
248 | return this.__INPUTS.every(input => {
|
249 | if (!input.signatures) return true;
|
250 | return input.signatures.every(signature => {
|
251 | if (!signature) return true;
|
252 | const hashType = signatureHashType(signature);
|
253 |
|
254 |
|
255 | return (
|
256 | (hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY) !== 0
|
257 | );
|
258 | });
|
259 | });
|
260 | }
|
261 | __needsOutputs(signingHashType) {
|
262 | if (signingHashType === transaction_1.Transaction.SIGHASH_ALL) {
|
263 | return this.__TX.outs.length === 0;
|
264 | }
|
265 |
|
266 |
|
267 | return (
|
268 | this.__TX.outs.length === 0 &&
|
269 | this.__INPUTS.some(input => {
|
270 | if (!input.signatures) return false;
|
271 | return input.signatures.some(signature => {
|
272 | if (!signature) return false;
|
273 | const hashType = signatureHashType(signature);
|
274 | if (hashType & transaction_1.Transaction.SIGHASH_NONE) return false;
|
275 | return true;
|
276 | });
|
277 | })
|
278 | );
|
279 | }
|
280 | __canModifyOutputs() {
|
281 | const nInputs = this.__TX.ins.length;
|
282 | const nOutputs = this.__TX.outs.length;
|
283 | return this.__INPUTS.every(input => {
|
284 | if (input.signatures === undefined) return true;
|
285 | return input.signatures.every(signature => {
|
286 | if (!signature) return true;
|
287 | const hashType = signatureHashType(signature);
|
288 | const hashTypeMod = hashType & 0x1f;
|
289 | if (hashTypeMod === transaction_1.Transaction.SIGHASH_NONE) return true;
|
290 | if (hashTypeMod === transaction_1.Transaction.SIGHASH_SINGLE) {
|
291 |
|
292 |
|
293 |
|
294 | return nInputs <= nOutputs;
|
295 | }
|
296 | return false;
|
297 | });
|
298 | });
|
299 | }
|
300 | __overMaximumFees(bytes) {
|
301 |
|
302 | const incoming = this.__INPUTS.reduce((a, x) => a + (x.value >>> 0), 0);
|
303 |
|
304 |
|
305 | const outgoing = this.__TX.outs.reduce((a, x) => a + x.value, 0);
|
306 | const fee = incoming - outgoing;
|
307 | const feeRate = fee / bytes;
|
308 | return feeRate > this.maximumFeeRate;
|
309 | }
|
310 | }
|
311 | exports.TransactionBuilder = TransactionBuilder;
|
312 | function expandInput(scriptSig, witnessStack, type, scriptPubKey) {
|
313 | if (scriptSig.length === 0 && witnessStack.length === 0) return {};
|
314 | if (!type) {
|
315 | let ssType = classify.input(scriptSig, true);
|
316 | let wsType = classify.witness(witnessStack, true);
|
317 | if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined;
|
318 | if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined;
|
319 | type = ssType || wsType;
|
320 | }
|
321 | switch (type) {
|
322 | case SCRIPT_TYPES.P2WPKH: {
|
323 | const { output, pubkey, signature } = payments.p2wpkh({
|
324 | witness: witnessStack,
|
325 | });
|
326 | return {
|
327 | prevOutScript: output,
|
328 | prevOutType: SCRIPT_TYPES.P2WPKH,
|
329 | pubkeys: [pubkey],
|
330 | signatures: [signature],
|
331 | };
|
332 | }
|
333 | case SCRIPT_TYPES.P2PKH: {
|
334 | const { output, pubkey, signature } = payments.p2pkh({
|
335 | input: scriptSig,
|
336 | });
|
337 | return {
|
338 | prevOutScript: output,
|
339 | prevOutType: SCRIPT_TYPES.P2PKH,
|
340 | pubkeys: [pubkey],
|
341 | signatures: [signature],
|
342 | };
|
343 | }
|
344 | case SCRIPT_TYPES.P2PK: {
|
345 | const { signature } = payments.p2pk({ input: scriptSig });
|
346 | return {
|
347 | prevOutType: SCRIPT_TYPES.P2PK,
|
348 | pubkeys: [undefined],
|
349 | signatures: [signature],
|
350 | };
|
351 | }
|
352 | case SCRIPT_TYPES.P2MS: {
|
353 | const { m, pubkeys, signatures } = payments.p2ms(
|
354 | {
|
355 | input: scriptSig,
|
356 | output: scriptPubKey,
|
357 | },
|
358 | { allowIncomplete: true },
|
359 | );
|
360 | return {
|
361 | prevOutType: SCRIPT_TYPES.P2MS,
|
362 | pubkeys,
|
363 | signatures,
|
364 | maxSignatures: m,
|
365 | };
|
366 | }
|
367 | }
|
368 | if (type === SCRIPT_TYPES.P2SH) {
|
369 | const { output, redeem } = payments.p2sh({
|
370 | input: scriptSig,
|
371 | witness: witnessStack,
|
372 | });
|
373 | const outputType = classify.output(redeem.output);
|
374 | const expanded = expandInput(
|
375 | redeem.input,
|
376 | redeem.witness,
|
377 | outputType,
|
378 | redeem.output,
|
379 | );
|
380 | if (!expanded.prevOutType) return {};
|
381 | return {
|
382 | prevOutScript: output,
|
383 | prevOutType: SCRIPT_TYPES.P2SH,
|
384 | redeemScript: redeem.output,
|
385 | redeemScriptType: expanded.prevOutType,
|
386 | witnessScript: expanded.witnessScript,
|
387 | witnessScriptType: expanded.witnessScriptType,
|
388 | pubkeys: expanded.pubkeys,
|
389 | signatures: expanded.signatures,
|
390 | };
|
391 | }
|
392 | if (type === SCRIPT_TYPES.P2WSH) {
|
393 | const { output, redeem } = payments.p2wsh({
|
394 | input: scriptSig,
|
395 | witness: witnessStack,
|
396 | });
|
397 | const outputType = classify.output(redeem.output);
|
398 | let expanded;
|
399 | if (outputType === SCRIPT_TYPES.P2WPKH) {
|
400 | expanded = expandInput(redeem.input, redeem.witness, outputType);
|
401 | } else {
|
402 | expanded = expandInput(
|
403 | bscript.compile(redeem.witness),
|
404 | [],
|
405 | outputType,
|
406 | redeem.output,
|
407 | );
|
408 | }
|
409 | if (!expanded.prevOutType) return {};
|
410 | return {
|
411 | prevOutScript: output,
|
412 | prevOutType: SCRIPT_TYPES.P2WSH,
|
413 | witnessScript: redeem.output,
|
414 | witnessScriptType: expanded.prevOutType,
|
415 | pubkeys: expanded.pubkeys,
|
416 | signatures: expanded.signatures,
|
417 | };
|
418 | }
|
419 | return {
|
420 | prevOutType: SCRIPT_TYPES.NONSTANDARD,
|
421 | prevOutScript: scriptSig,
|
422 | };
|
423 | }
|
424 |
|
425 | function fixMultisigOrder(input, transaction, vin) {
|
426 | if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript)
|
427 | return;
|
428 | if (input.pubkeys.length === input.signatures.length) return;
|
429 | const unmatched = input.signatures.concat();
|
430 | input.signatures = input.pubkeys.map(pubKey => {
|
431 | const keyPair = ECPair.fromPublicKey(pubKey);
|
432 | let match;
|
433 |
|
434 | unmatched.some((signature, i) => {
|
435 |
|
436 | if (!signature) return false;
|
437 |
|
438 | const parsed = bscript.signature.decode(signature);
|
439 | const hash = transaction.hashForSignature(
|
440 | vin,
|
441 | input.redeemScript,
|
442 | parsed.hashType,
|
443 | );
|
444 |
|
445 | if (!keyPair.verify(hash, parsed.signature)) return false;
|
446 |
|
447 | unmatched[i] = undefined;
|
448 | match = signature;
|
449 | return true;
|
450 | });
|
451 | return match;
|
452 | });
|
453 | }
|
454 | function expandOutput(script, ourPubKey) {
|
455 | typeforce(types.Buffer, script);
|
456 | const type = classify.output(script);
|
457 | switch (type) {
|
458 | case SCRIPT_TYPES.P2PKH: {
|
459 | if (!ourPubKey) return { type };
|
460 |
|
461 | const pkh1 = payments.p2pkh({ output: script }).hash;
|
462 | const pkh2 = bcrypto.hash160(ourPubKey);
|
463 | if (!pkh1.equals(pkh2)) return { type };
|
464 | return {
|
465 | type,
|
466 | pubkeys: [ourPubKey],
|
467 | signatures: [undefined],
|
468 | };
|
469 | }
|
470 | case SCRIPT_TYPES.P2WPKH: {
|
471 | if (!ourPubKey) return { type };
|
472 |
|
473 | const wpkh1 = payments.p2wpkh({ output: script }).hash;
|
474 | const wpkh2 = bcrypto.hash160(ourPubKey);
|
475 | if (!wpkh1.equals(wpkh2)) return { type };
|
476 | return {
|
477 | type,
|
478 | pubkeys: [ourPubKey],
|
479 | signatures: [undefined],
|
480 | };
|
481 | }
|
482 | case SCRIPT_TYPES.P2PK: {
|
483 | const p2pk = payments.p2pk({ output: script });
|
484 | return {
|
485 | type,
|
486 | pubkeys: [p2pk.pubkey],
|
487 | signatures: [undefined],
|
488 | };
|
489 | }
|
490 | case SCRIPT_TYPES.P2MS: {
|
491 | const p2ms = payments.p2ms({ output: script });
|
492 | return {
|
493 | type,
|
494 | pubkeys: p2ms.pubkeys,
|
495 | signatures: p2ms.pubkeys.map(() => undefined),
|
496 | maxSignatures: p2ms.m,
|
497 | };
|
498 | }
|
499 | }
|
500 | return { type };
|
501 | }
|
502 | function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
|
503 | if (redeemScript && witnessScript) {
|
504 | const p2wsh = payments.p2wsh({
|
505 | redeem: { output: witnessScript },
|
506 | });
|
507 | const p2wshAlt = payments.p2wsh({ output: redeemScript });
|
508 | const p2sh = payments.p2sh({ redeem: { output: redeemScript } });
|
509 | const p2shAlt = payments.p2sh({ redeem: p2wsh });
|
510 |
|
511 | if (!p2wsh.hash.equals(p2wshAlt.hash))
|
512 | throw new Error('Witness script inconsistent with prevOutScript');
|
513 | if (!p2sh.hash.equals(p2shAlt.hash))
|
514 | throw new Error('Redeem script inconsistent with prevOutScript');
|
515 | const expanded = expandOutput(p2wsh.redeem.output, ourPubKey);
|
516 | if (!expanded.pubkeys)
|
517 | throw new Error(
|
518 | expanded.type +
|
519 | ' not supported as witnessScript (' +
|
520 | bscript.toASM(witnessScript) +
|
521 | ')',
|
522 | );
|
523 | if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
524 | expanded.signatures = input.signatures;
|
525 | }
|
526 | const signScript = witnessScript;
|
527 | if (expanded.type === SCRIPT_TYPES.P2WPKH)
|
528 | throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure');
|
529 | return {
|
530 | redeemScript,
|
531 | redeemScriptType: SCRIPT_TYPES.P2WSH,
|
532 | witnessScript,
|
533 | witnessScriptType: expanded.type,
|
534 | prevOutType: SCRIPT_TYPES.P2SH,
|
535 | prevOutScript: p2sh.output,
|
536 | hasWitness: true,
|
537 | signScript,
|
538 | signType: expanded.type,
|
539 | pubkeys: expanded.pubkeys,
|
540 | signatures: expanded.signatures,
|
541 | maxSignatures: expanded.maxSignatures,
|
542 | };
|
543 | }
|
544 | if (redeemScript) {
|
545 | const p2sh = payments.p2sh({ redeem: { output: redeemScript } });
|
546 | if (input.prevOutScript) {
|
547 | let p2shAlt;
|
548 | try {
|
549 | p2shAlt = payments.p2sh({ output: input.prevOutScript });
|
550 | } catch (e) {
|
551 | throw new Error('PrevOutScript must be P2SH');
|
552 | }
|
553 | if (!p2sh.hash.equals(p2shAlt.hash))
|
554 | throw new Error('Redeem script inconsistent with prevOutScript');
|
555 | }
|
556 | const expanded = expandOutput(p2sh.redeem.output, ourPubKey);
|
557 | if (!expanded.pubkeys)
|
558 | throw new Error(
|
559 | expanded.type +
|
560 | ' not supported as redeemScript (' +
|
561 | bscript.toASM(redeemScript) +
|
562 | ')',
|
563 | );
|
564 | if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
565 | expanded.signatures = input.signatures;
|
566 | }
|
567 | let signScript = redeemScript;
|
568 | if (expanded.type === SCRIPT_TYPES.P2WPKH) {
|
569 | signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output;
|
570 | }
|
571 | return {
|
572 | redeemScript,
|
573 | redeemScriptType: expanded.type,
|
574 | prevOutType: SCRIPT_TYPES.P2SH,
|
575 | prevOutScript: p2sh.output,
|
576 | hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH,
|
577 | signScript,
|
578 | signType: expanded.type,
|
579 | pubkeys: expanded.pubkeys,
|
580 | signatures: expanded.signatures,
|
581 | maxSignatures: expanded.maxSignatures,
|
582 | };
|
583 | }
|
584 | if (witnessScript) {
|
585 | const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } });
|
586 | if (input.prevOutScript) {
|
587 | const p2wshAlt = payments.p2wsh({ output: input.prevOutScript });
|
588 | if (!p2wsh.hash.equals(p2wshAlt.hash))
|
589 | throw new Error('Witness script inconsistent with prevOutScript');
|
590 | }
|
591 | const expanded = expandOutput(p2wsh.redeem.output, ourPubKey);
|
592 | if (!expanded.pubkeys)
|
593 | throw new Error(
|
594 | expanded.type +
|
595 | ' not supported as witnessScript (' +
|
596 | bscript.toASM(witnessScript) +
|
597 | ')',
|
598 | );
|
599 | if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
600 | expanded.signatures = input.signatures;
|
601 | }
|
602 | const signScript = witnessScript;
|
603 | if (expanded.type === SCRIPT_TYPES.P2WPKH)
|
604 | throw new Error('P2WSH(P2WPKH) is a consensus failure');
|
605 | return {
|
606 | witnessScript,
|
607 | witnessScriptType: expanded.type,
|
608 | prevOutType: SCRIPT_TYPES.P2WSH,
|
609 | prevOutScript: p2wsh.output,
|
610 | hasWitness: true,
|
611 | signScript,
|
612 | signType: expanded.type,
|
613 | pubkeys: expanded.pubkeys,
|
614 | signatures: expanded.signatures,
|
615 | maxSignatures: expanded.maxSignatures,
|
616 | };
|
617 | }
|
618 | if (input.prevOutType && input.prevOutScript) {
|
619 |
|
620 | if (input.prevOutType === SCRIPT_TYPES.P2SH)
|
621 | throw new Error(
|
622 | 'PrevOutScript is ' + input.prevOutType + ', requires redeemScript',
|
623 | );
|
624 | if (input.prevOutType === SCRIPT_TYPES.P2WSH)
|
625 | throw new Error(
|
626 | 'PrevOutScript is ' + input.prevOutType + ', requires witnessScript',
|
627 | );
|
628 | if (!input.prevOutScript) throw new Error('PrevOutScript is missing');
|
629 | const expanded = expandOutput(input.prevOutScript, ourPubKey);
|
630 | if (!expanded.pubkeys)
|
631 | throw new Error(
|
632 | expanded.type +
|
633 | ' not supported (' +
|
634 | bscript.toASM(input.prevOutScript) +
|
635 | ')',
|
636 | );
|
637 | if (input.signatures && input.signatures.some(x => x !== undefined)) {
|
638 | expanded.signatures = input.signatures;
|
639 | }
|
640 | let signScript = input.prevOutScript;
|
641 | if (expanded.type === SCRIPT_TYPES.P2WPKH) {
|
642 | signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output;
|
643 | }
|
644 | return {
|
645 | prevOutType: expanded.type,
|
646 | prevOutScript: input.prevOutScript,
|
647 | hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH,
|
648 | signScript,
|
649 | signType: expanded.type,
|
650 | pubkeys: expanded.pubkeys,
|
651 | signatures: expanded.signatures,
|
652 | maxSignatures: expanded.maxSignatures,
|
653 | };
|
654 | }
|
655 | const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output;
|
656 | return {
|
657 | prevOutType: SCRIPT_TYPES.P2PKH,
|
658 | prevOutScript,
|
659 | hasWitness: false,
|
660 | signScript: prevOutScript,
|
661 | signType: SCRIPT_TYPES.P2PKH,
|
662 | pubkeys: [ourPubKey],
|
663 | signatures: [undefined],
|
664 | };
|
665 | }
|
666 | function build(type, input, allowIncomplete) {
|
667 | const pubkeys = input.pubkeys || [];
|
668 | let signatures = input.signatures || [];
|
669 | switch (type) {
|
670 | case SCRIPT_TYPES.P2PKH: {
|
671 | if (pubkeys.length === 0) break;
|
672 | if (signatures.length === 0) break;
|
673 | return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] });
|
674 | }
|
675 | case SCRIPT_TYPES.P2WPKH: {
|
676 | if (pubkeys.length === 0) break;
|
677 | if (signatures.length === 0) break;
|
678 | return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] });
|
679 | }
|
680 | case SCRIPT_TYPES.P2PK: {
|
681 | if (pubkeys.length === 0) break;
|
682 | if (signatures.length === 0) break;
|
683 | return payments.p2pk({ signature: signatures[0] });
|
684 | }
|
685 | case SCRIPT_TYPES.P2MS: {
|
686 | const m = input.maxSignatures;
|
687 | if (allowIncomplete) {
|
688 | signatures = signatures.map(x => x || script_1.OPS.OP_0);
|
689 | } else {
|
690 | signatures = signatures.filter(x => x);
|
691 | }
|
692 |
|
693 |
|
694 | const validate = !allowIncomplete || m === signatures.length;
|
695 | return payments.p2ms(
|
696 | { m, pubkeys, signatures },
|
697 | { allowIncomplete, validate },
|
698 | );
|
699 | }
|
700 | case SCRIPT_TYPES.P2SH: {
|
701 | const redeem = build(input.redeemScriptType, input, allowIncomplete);
|
702 | if (!redeem) return;
|
703 | return payments.p2sh({
|
704 | redeem: {
|
705 | output: redeem.output || input.redeemScript,
|
706 | input: redeem.input,
|
707 | witness: redeem.witness,
|
708 | },
|
709 | });
|
710 | }
|
711 | case SCRIPT_TYPES.P2WSH: {
|
712 | const redeem = build(input.witnessScriptType, input, allowIncomplete);
|
713 | if (!redeem) return;
|
714 | return payments.p2wsh({
|
715 | redeem: {
|
716 | output: input.witnessScript,
|
717 | input: redeem.input,
|
718 | witness: redeem.witness,
|
719 | },
|
720 | });
|
721 | }
|
722 | }
|
723 | }
|
724 | function canSign(input) {
|
725 | return (
|
726 | input.signScript !== undefined &&
|
727 | input.signType !== undefined &&
|
728 | input.pubkeys !== undefined &&
|
729 | input.signatures !== undefined &&
|
730 | input.signatures.length === input.pubkeys.length &&
|
731 | input.pubkeys.length > 0 &&
|
732 | (input.hasWitness === false || input.value !== undefined)
|
733 | );
|
734 | }
|
735 | function signatureHashType(buffer) {
|
736 | return buffer.readUInt8(buffer.length - 1);
|
737 | }
|
738 | function checkSignArgs(inputs, signParams) {
|
739 | if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) {
|
740 | throw new TypeError(
|
741 | `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`,
|
742 | );
|
743 | }
|
744 | tfMessage(
|
745 | typeforce.Number,
|
746 | signParams.vin,
|
747 | `sign must include vin parameter as Number (input index)`,
|
748 | );
|
749 | tfMessage(
|
750 | types.Signer,
|
751 | signParams.keyPair,
|
752 | `sign must include keyPair parameter as Signer interface`,
|
753 | );
|
754 | tfMessage(
|
755 | typeforce.maybe(typeforce.Number),
|
756 | signParams.hashType,
|
757 | `sign hashType parameter must be a number`,
|
758 | );
|
759 | const prevOutType = (inputs[signParams.vin] || []).prevOutType;
|
760 | const posType = signParams.prevOutScriptType;
|
761 | switch (posType) {
|
762 | case 'p2pkh':
|
763 | if (prevOutType && prevOutType !== 'pubkeyhash') {
|
764 | throw new TypeError(
|
765 | `input #${signParams.vin} is not of type p2pkh: ${prevOutType}`,
|
766 | );
|
767 | }
|
768 | tfMessage(
|
769 | typeforce.value(undefined),
|
770 | signParams.witnessScript,
|
771 | `${posType} requires NO witnessScript`,
|
772 | );
|
773 | tfMessage(
|
774 | typeforce.value(undefined),
|
775 | signParams.redeemScript,
|
776 | `${posType} requires NO redeemScript`,
|
777 | );
|
778 | tfMessage(
|
779 | typeforce.value(undefined),
|
780 | signParams.witnessValue,
|
781 | `${posType} requires NO witnessValue`,
|
782 | );
|
783 | break;
|
784 | case 'p2pk':
|
785 | if (prevOutType && prevOutType !== 'pubkey') {
|
786 | throw new TypeError(
|
787 | `input #${signParams.vin} is not of type p2pk: ${prevOutType}`,
|
788 | );
|
789 | }
|
790 | tfMessage(
|
791 | typeforce.value(undefined),
|
792 | signParams.witnessScript,
|
793 | `${posType} requires NO witnessScript`,
|
794 | );
|
795 | tfMessage(
|
796 | typeforce.value(undefined),
|
797 | signParams.redeemScript,
|
798 | `${posType} requires NO redeemScript`,
|
799 | );
|
800 | tfMessage(
|
801 | typeforce.value(undefined),
|
802 | signParams.witnessValue,
|
803 | `${posType} requires NO witnessValue`,
|
804 | );
|
805 | break;
|
806 | case 'p2wpkh':
|
807 | if (prevOutType && prevOutType !== 'witnesspubkeyhash') {
|
808 | throw new TypeError(
|
809 | `input #${signParams.vin} is not of type p2wpkh: ${prevOutType}`,
|
810 | );
|
811 | }
|
812 | tfMessage(
|
813 | typeforce.value(undefined),
|
814 | signParams.witnessScript,
|
815 | `${posType} requires NO witnessScript`,
|
816 | );
|
817 | tfMessage(
|
818 | typeforce.value(undefined),
|
819 | signParams.redeemScript,
|
820 | `${posType} requires NO redeemScript`,
|
821 | );
|
822 | tfMessage(
|
823 | types.Satoshi,
|
824 | signParams.witnessValue,
|
825 | `${posType} requires witnessValue`,
|
826 | );
|
827 | break;
|
828 | case 'p2ms':
|
829 | if (prevOutType && prevOutType !== 'multisig') {
|
830 | throw new TypeError(
|
831 | `input #${signParams.vin} is not of type p2ms: ${prevOutType}`,
|
832 | );
|
833 | }
|
834 | tfMessage(
|
835 | typeforce.value(undefined),
|
836 | signParams.witnessScript,
|
837 | `${posType} requires NO witnessScript`,
|
838 | );
|
839 | tfMessage(
|
840 | typeforce.value(undefined),
|
841 | signParams.redeemScript,
|
842 | `${posType} requires NO redeemScript`,
|
843 | );
|
844 | tfMessage(
|
845 | typeforce.value(undefined),
|
846 | signParams.witnessValue,
|
847 | `${posType} requires NO witnessValue`,
|
848 | );
|
849 | break;
|
850 | case 'p2sh-p2wpkh':
|
851 | if (prevOutType && prevOutType !== 'scripthash') {
|
852 | throw new TypeError(
|
853 | `input #${signParams.vin} is not of type p2sh-p2wpkh: ${prevOutType}`,
|
854 | );
|
855 | }
|
856 | tfMessage(
|
857 | typeforce.value(undefined),
|
858 | signParams.witnessScript,
|
859 | `${posType} requires NO witnessScript`,
|
860 | );
|
861 | tfMessage(
|
862 | typeforce.Buffer,
|
863 | signParams.redeemScript,
|
864 | `${posType} requires redeemScript`,
|
865 | );
|
866 | tfMessage(
|
867 | types.Satoshi,
|
868 | signParams.witnessValue,
|
869 | `${posType} requires witnessValue`,
|
870 | );
|
871 | break;
|
872 | case 'p2sh-p2ms':
|
873 | case 'p2sh-p2pk':
|
874 | case 'p2sh-p2pkh':
|
875 | if (prevOutType && prevOutType !== 'scripthash') {
|
876 | throw new TypeError(
|
877 | `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`,
|
878 | );
|
879 | }
|
880 | tfMessage(
|
881 | typeforce.value(undefined),
|
882 | signParams.witnessScript,
|
883 | `${posType} requires NO witnessScript`,
|
884 | );
|
885 | tfMessage(
|
886 | typeforce.Buffer,
|
887 | signParams.redeemScript,
|
888 | `${posType} requires redeemScript`,
|
889 | );
|
890 | tfMessage(
|
891 | typeforce.value(undefined),
|
892 | signParams.witnessValue,
|
893 | `${posType} requires NO witnessValue`,
|
894 | );
|
895 | break;
|
896 | case 'p2wsh-p2ms':
|
897 | case 'p2wsh-p2pk':
|
898 | case 'p2wsh-p2pkh':
|
899 | if (prevOutType && prevOutType !== 'witnessscripthash') {
|
900 | throw new TypeError(
|
901 | `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`,
|
902 | );
|
903 | }
|
904 | tfMessage(
|
905 | typeforce.Buffer,
|
906 | signParams.witnessScript,
|
907 | `${posType} requires witnessScript`,
|
908 | );
|
909 | tfMessage(
|
910 | typeforce.value(undefined),
|
911 | signParams.redeemScript,
|
912 | `${posType} requires NO redeemScript`,
|
913 | );
|
914 | tfMessage(
|
915 | types.Satoshi,
|
916 | signParams.witnessValue,
|
917 | `${posType} requires witnessValue`,
|
918 | );
|
919 | break;
|
920 | case 'p2sh-p2wsh-p2ms':
|
921 | case 'p2sh-p2wsh-p2pk':
|
922 | case 'p2sh-p2wsh-p2pkh':
|
923 | if (prevOutType && prevOutType !== 'scripthash') {
|
924 | throw new TypeError(
|
925 | `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`,
|
926 | );
|
927 | }
|
928 | tfMessage(
|
929 | typeforce.Buffer,
|
930 | signParams.witnessScript,
|
931 | `${posType} requires witnessScript`,
|
932 | );
|
933 | tfMessage(
|
934 | typeforce.Buffer,
|
935 | signParams.redeemScript,
|
936 | `${posType} requires witnessScript`,
|
937 | );
|
938 | tfMessage(
|
939 | types.Satoshi,
|
940 | signParams.witnessValue,
|
941 | `${posType} requires witnessScript`,
|
942 | );
|
943 | break;
|
944 | }
|
945 | }
|
946 | function trySign({
|
947 | input,
|
948 | ourPubKey,
|
949 | keyPair,
|
950 | signatureHash,
|
951 | hashType,
|
952 | useLowR,
|
953 | }) {
|
954 |
|
955 | let signed = false;
|
956 | for (const [i, pubKey] of input.pubkeys.entries()) {
|
957 | if (!ourPubKey.equals(pubKey)) continue;
|
958 | if (input.signatures[i]) throw new Error('Signature already exists');
|
959 |
|
960 | if (ourPubKey.length !== 33 && input.hasWitness) {
|
961 | throw new Error(
|
962 | 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH',
|
963 | );
|
964 | }
|
965 | const signature = keyPair.sign(signatureHash, useLowR);
|
966 | input.signatures[i] = bscript.signature.encode(signature, hashType);
|
967 | signed = true;
|
968 | }
|
969 | if (!signed) throw new Error('Key pair cannot sign for this input');
|
970 | }
|
971 | function getSigningData(
|
972 | network,
|
973 | inputs,
|
974 | needsOutputs,
|
975 | tx,
|
976 | signParams,
|
977 | keyPair,
|
978 | redeemScript,
|
979 | hashType,
|
980 | witnessValue,
|
981 | witnessScript,
|
982 | useLowR,
|
983 | ) {
|
984 | let vin;
|
985 | if (typeof signParams === 'number') {
|
986 | console.warn(
|
987 | 'DEPRECATED: TransactionBuilder sign method arguments ' +
|
988 | 'will change in v6, please use the TxbSignArg interface',
|
989 | );
|
990 | vin = signParams;
|
991 | } else if (typeof signParams === 'object') {
|
992 | checkSignArgs(inputs, signParams);
|
993 | ({
|
994 | vin,
|
995 | keyPair,
|
996 | redeemScript,
|
997 | hashType,
|
998 | witnessValue,
|
999 | witnessScript,
|
1000 | } = signParams);
|
1001 | } else {
|
1002 | throw new TypeError(
|
1003 | 'TransactionBuilder sign first arg must be TxbSignArg or number',
|
1004 | );
|
1005 | }
|
1006 | if (keyPair === undefined) {
|
1007 | throw new Error('sign requires keypair');
|
1008 | }
|
1009 |
|
1010 | if (keyPair.network && keyPair.network !== network)
|
1011 | throw new TypeError('Inconsistent network');
|
1012 | if (!inputs[vin]) throw new Error('No input at index: ' + vin);
|
1013 | hashType = hashType || transaction_1.Transaction.SIGHASH_ALL;
|
1014 | if (needsOutputs(hashType)) throw new Error('Transaction needs outputs');
|
1015 | const input = inputs[vin];
|
1016 |
|
1017 | if (
|
1018 | input.redeemScript !== undefined &&
|
1019 | redeemScript &&
|
1020 | !input.redeemScript.equals(redeemScript)
|
1021 | ) {
|
1022 | throw new Error('Inconsistent redeemScript');
|
1023 | }
|
1024 | const ourPubKey =
|
1025 | keyPair.publicKey || (keyPair.getPublicKey && keyPair.getPublicKey());
|
1026 | if (!canSign(input)) {
|
1027 | if (witnessValue !== undefined) {
|
1028 | if (input.value !== undefined && input.value !== witnessValue)
|
1029 | throw new Error('Input did not match witnessValue');
|
1030 | typeforce(types.Satoshi, witnessValue);
|
1031 | input.value = witnessValue;
|
1032 | }
|
1033 | if (!canSign(input)) {
|
1034 | const prepared = prepareInput(
|
1035 | input,
|
1036 | ourPubKey,
|
1037 | redeemScript,
|
1038 | witnessScript,
|
1039 | );
|
1040 |
|
1041 | Object.assign(input, prepared);
|
1042 | }
|
1043 | if (!canSign(input)) throw Error(input.prevOutType + ' not supported');
|
1044 | }
|
1045 |
|
1046 | let signatureHash;
|
1047 | if (input.hasWitness) {
|
1048 | signatureHash = tx.hashForWitnessV0(
|
1049 | vin,
|
1050 | input.signScript,
|
1051 | input.value,
|
1052 | hashType,
|
1053 | );
|
1054 | } else {
|
1055 | signatureHash = tx.hashForSignature(vin, input.signScript, hashType);
|
1056 | }
|
1057 | return {
|
1058 | input,
|
1059 | ourPubKey,
|
1060 | keyPair,
|
1061 | signatureHash,
|
1062 | hashType,
|
1063 | useLowR: !!useLowR,
|
1064 | };
|
1065 | }
|