UNPKG

42.3 kBJavaScriptView Raw
1'use strict';
2
3var _ = require('lodash');
4var $ = require('../util/preconditions');
5var buffer = require('buffer');
6var compare = Buffer.compare || require('buffer-compare');
7
8var errors = require('../errors');
9var BufferUtil = require('../util/buffer');
10var JSUtil = require('../util/js');
11var BufferReader = require('../encoding/bufferreader');
12var BufferWriter = require('../encoding/bufferwriter');
13var Hash = require('../crypto/hash');
14var Signature = require('../crypto/signature');
15var Sighash = require('./sighash');
16var SighashWitness = require('./sighashwitness');
17
18var Address = require('../address');
19var UnspentOutput = require('./unspentoutput');
20var Input = require('./input');
21var PublicKeyHashInput = Input.PublicKeyHash;
22var PublicKeyInput = Input.PublicKey;
23var MultiSigScriptHashInput = Input.MultiSigScriptHash;
24var MultiSigInput = Input.MultiSig;
25var Output = require('./output');
26var Script = require('../script');
27var PrivateKey = require('../privatekey');
28var BN = require('../crypto/bn');
29
30/**
31 * Represents a transaction, a set of inputs and outputs to change ownership of tokens
32 *
33 * @param {*} serialized
34 * @constructor
35 */
36function Transaction(serialized, opts) {
37 if (!(this instanceof Transaction)) {
38 return new Transaction(serialized);
39 }
40 this.inputs = [];
41 this.outputs = [];
42 this._inputAmount = undefined;
43 this._outputAmount = undefined;
44
45 if (serialized) {
46 if (serialized instanceof Transaction) {
47 return Transaction.shallowCopy(serialized);
48 } else if (JSUtil.isHexa(serialized)) {
49 this.fromString(serialized);
50 } else if (BufferUtil.isBuffer(serialized)) {
51 this.fromBuffer(serialized);
52 } else if (_.isObject(serialized)) {
53 this.fromObject(serialized, opts);
54 } else {
55 throw new errors.InvalidArgument('Must provide an object or string to deserialize a transaction');
56 }
57 } else {
58 this._newTransaction();
59 }
60}
61var CURRENT_VERSION = 2;
62var DEFAULT_NLOCKTIME = 0;
63var MAX_BLOCK_SIZE = 1000000;
64
65// Minimum amount for an output for it not to be considered a dust output
66Transaction.DUST_AMOUNT = 546;
67
68// Margin of error to allow fees in the vecinity of the expected value but doesn't allow a big difference
69Transaction.FEE_SECURITY_MARGIN = 150;
70
71// max amount of satoshis in circulation
72Transaction.MAX_MONEY = 21000000 * 1e8;
73
74// nlocktime limit to be considered block height rather than a timestamp
75Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT = 5e8;
76
77// Max value for an unsigned 32 bit value
78Transaction.NLOCKTIME_MAX_VALUE = 4294967295;
79
80// Value used for fee estimation (satoshis per kilobyte)
81Transaction.FEE_PER_KB = 100000;
82
83// Safe upper bound for change address script size in bytes
84Transaction.CHANGE_OUTPUT_MAX_SIZE = 20 + 4 + 34 + 4;
85Transaction.MAXIMUM_EXTRA_SIZE = 4 + 9 + 9 + 4;
86
87/* Constructors and Serialization */
88
89/**
90 * Create a 'shallow' copy of the transaction, by serializing and deserializing
91 * it dropping any additional information that inputs and outputs may have hold
92 *
93 * @param {Transaction} transaction
94 * @return {Transaction}
95 */
96Transaction.shallowCopy = function(transaction) {
97 var copy = new Transaction(transaction.toBuffer());
98 return copy;
99};
100
101var hashProperty = {
102 configurable: false,
103 enumerable: true,
104 get: function() {
105 this._hash = new BufferReader(this._getHash()).readReverse().toString('hex');
106 return this._hash;
107 }
108};
109
110var witnessHashProperty = {
111 configurable: false,
112 enumerable: true,
113 get: function() {
114 return new BufferReader(this._getWitnessHash()).readReverse().toString('hex');
115 }
116};
117
118Object.defineProperty(Transaction.prototype, 'witnessHash', witnessHashProperty);
119Object.defineProperty(Transaction.prototype, 'hash', hashProperty);
120Object.defineProperty(Transaction.prototype, 'id', hashProperty);
121
122var ioProperty = {
123 configurable: false,
124 enumerable: true,
125 get: function() {
126 return this._getInputAmount();
127 }
128};
129Object.defineProperty(Transaction.prototype, 'inputAmount', ioProperty);
130ioProperty.get = function() {
131 return this._getOutputAmount();
132};
133Object.defineProperty(Transaction.prototype, 'outputAmount', ioProperty);
134
135/**
136 * Retrieve the little endian hash of the transaction (used for serialization)
137 * @return {Buffer}
138 */
139Transaction.prototype._getHash = function() {
140 return Hash.sha256sha256(this.toBuffer(true));
141};
142
143/**
144 * Retrieve the little endian hash of the transaction including witness data
145 * @return {Buffer}
146 */
147Transaction.prototype._getWitnessHash = function() {
148 return Hash.sha256sha256(this.toBuffer(false));
149};
150
151/**
152 * Retrieve a hexa string that can be used with bitcoind's CLI interface
153 * (decoderawtransaction, sendrawtransaction)
154 *
155 * @param {Object|boolean=} unsafe if true, skip all tests. if it's an object,
156 * it's expected to contain a set of flags to skip certain tests:
157 * * `disableAll`: disable all checks
158 * * `disableSmallFees`: disable checking for fees that are too small
159 * * `disableLargeFees`: disable checking for fees that are too large
160 * * `disableIsFullySigned`: disable checking if all inputs are fully signed
161 * * `disableDustOutputs`: disable checking if there are no outputs that are dust amounts
162 * * `disableMoreOutputThanInput`: disable checking if the transaction spends more bitcoins than the sum of the input amounts
163 * @return {string}
164 */
165Transaction.prototype.serialize = function(unsafe) {
166 if (true === unsafe || unsafe && unsafe.disableAll) {
167 return this.uncheckedSerialize();
168 } else {
169 return this.checkedSerialize(unsafe);
170 }
171};
172
173Transaction.prototype.uncheckedSerialize = Transaction.prototype.toString = function() {
174 return this.toBuffer().toString('hex');
175};
176
177/**
178 * Retrieve a hexa string that can be used with bitcoind's CLI interface
179 * (decoderawtransaction, sendrawtransaction)
180 *
181 * @param {Object} opts allows to skip certain tests. {@see Transaction#serialize}
182 * @return {string}
183 */
184Transaction.prototype.checkedSerialize = function(opts) {
185 var serializationError = this.getSerializationError(opts);
186 if (serializationError) {
187 serializationError.message += ' - For more information please see: ' +
188 'https://bitcore.io/api/lib/transaction#serialization-checks';
189 throw serializationError;
190 }
191 return this.uncheckedSerialize();
192};
193
194Transaction.prototype.invalidSatoshis = function() {
195 var invalid = false;
196 for (var i = 0; i < this.outputs.length; i++) {
197 if (this.outputs[i].invalidSatoshis()) {
198 invalid = true;
199 }
200 }
201 return invalid;
202};
203
204/**
205 * Retrieve a possible error that could appear when trying to serialize and
206 * broadcast this transaction.
207 *
208 * @param {Object} opts allows to skip certain tests. {@see Transaction#serialize}
209 * @return {bitcore.Error}
210 */
211Transaction.prototype.getSerializationError = function(opts) {
212 opts = opts || {};
213
214 if (this.invalidSatoshis()) {
215 return new errors.Transaction.InvalidSatoshis();
216 }
217
218 var unspent = this._getUnspentValue();
219 var unspentError;
220 if (unspent < 0) {
221 if (!opts.disableMoreOutputThanInput) {
222 unspentError = new errors.Transaction.InvalidOutputAmountSum();
223 }
224 } else {
225 unspentError = this._hasFeeError(opts, unspent);
226 }
227
228 return unspentError ||
229 this._hasDustOutputs(opts) ||
230 this._isMissingSignatures(opts);
231};
232
233Transaction.prototype._hasFeeError = function(opts, unspent) {
234
235 if (!_.isUndefined(this._fee) && this._fee !== unspent) {
236 return new errors.Transaction.FeeError.Different(
237 'Unspent value is ' + unspent + ' but specified fee is ' + this._fee
238 );
239 }
240
241 if (!opts.disableLargeFees) {
242 var maximumFee = Math.floor(Transaction.FEE_SECURITY_MARGIN * this._estimateFee());
243 if (unspent > maximumFee) {
244 if (this._missingChange()) {
245 return new errors.Transaction.ChangeAddressMissing(
246 'Fee is too large and no change address was provided'
247 );
248 }
249 return new errors.Transaction.FeeError.TooLarge(
250 'expected less than ' + maximumFee + ' but got ' + unspent
251 );
252 }
253 }
254
255 if (!opts.disableSmallFees) {
256 var minimumFee = Math.ceil(this._estimateFee() / Transaction.FEE_SECURITY_MARGIN);
257 if (unspent < minimumFee) {
258 return new errors.Transaction.FeeError.TooSmall(
259 'expected more than ' + minimumFee + ' but got ' + unspent
260 );
261 }
262 }
263};
264
265Transaction.prototype._missingChange = function() {
266 return !this._changeScript;
267};
268
269Transaction.prototype._hasDustOutputs = function(opts) {
270 if (opts.disableDustOutputs) {
271 return;
272 }
273 var index, output;
274 for (index in this.outputs) {
275 output = this.outputs[index];
276 if (output.satoshis < Transaction.DUST_AMOUNT && !output.script.isDataOut()) {
277 return new errors.Transaction.DustOutputs();
278 }
279 }
280};
281
282Transaction.prototype._isMissingSignatures = function(opts) {
283 if (opts.disableIsFullySigned) {
284 return;
285 }
286 if (!this.isFullySigned()) {
287 return new errors.Transaction.MissingSignatures();
288 }
289};
290
291Transaction.prototype.inspect = function() {
292 return '<Transaction: ' + this.uncheckedSerialize() + '>';
293};
294
295Transaction.prototype.toBuffer = function(noWitness) {
296 var writer = new BufferWriter();
297 return this.toBufferWriter(writer, noWitness).toBuffer();
298};
299
300Transaction.prototype.hasWitnesses = function() {
301 for (var i = 0; i < this.inputs.length; i++) {
302 if (this.inputs[i].hasWitnesses()) {
303 return true;
304 }
305 }
306 return false;
307};
308
309Transaction.prototype.toBufferWriter = function(writer, noWitness) {
310 writer.writeInt32LE(this.version);
311
312 var hasWitnesses = this.hasWitnesses();
313
314 if (hasWitnesses && !noWitness) {
315 writer.write(Buffer.from('0001', 'hex'));
316 }
317
318 writer.writeVarintNum(this.inputs.length);
319
320 _.each(this.inputs, function(input) {
321 input.toBufferWriter(writer);
322 });
323
324 writer.writeVarintNum(this.outputs.length);
325 _.each(this.outputs, function(output) {
326 output.toBufferWriter(writer);
327 });
328
329 if (hasWitnesses && !noWitness) {
330 _.each(this.inputs, function(input) {
331 var witnesses = input.getWitnesses();
332 writer.writeVarintNum(witnesses.length);
333 for (var j = 0; j < witnesses.length; j++) {
334 writer.writeVarintNum(witnesses[j].length);
335 writer.write(witnesses[j]);
336 }
337 });
338 }
339
340 writer.writeUInt32LE(this.nLockTime);
341 return writer;
342};
343
344Transaction.prototype.fromBuffer = function(buffer) {
345 var reader = new BufferReader(buffer);
346 return this.fromBufferReader(reader);
347};
348
349Transaction.prototype.fromBufferReader = function(reader) {
350 $.checkArgument(!reader.finished(), 'No transaction data received');
351
352 this.version = reader.readInt32LE();
353 var sizeTxIns = reader.readVarintNum();
354
355 // check for segwit
356 var hasWitnesses = false;
357 if (sizeTxIns === 0 && reader.buf[reader.pos] !== 0) {
358 reader.pos += 1;
359 hasWitnesses = true;
360 sizeTxIns = reader.readVarintNum();
361 }
362
363 for (var i = 0; i < sizeTxIns; i++) {
364 var input = Input.fromBufferReader(reader);
365 this.inputs.push(input);
366 }
367
368 var sizeTxOuts = reader.readVarintNum();
369 for (var j = 0; j < sizeTxOuts; j++) {
370 this.outputs.push(Output.fromBufferReader(reader));
371 }
372
373 if (hasWitnesses) {
374 for (var k = 0; k < sizeTxIns; k++) {
375 var itemCount = reader.readVarintNum();
376 var witnesses = [];
377 for (var l = 0; l < itemCount; l++) {
378 var size = reader.readVarintNum();
379 var item = reader.read(size);
380 witnesses.push(item);
381 }
382 this.inputs[k].setWitnesses(witnesses);
383 }
384 }
385
386 this.nLockTime = reader.readUInt32LE();
387 return this;
388};
389
390
391Transaction.prototype.toObject = Transaction.prototype.toJSON = function toObject() {
392 var inputs = [];
393 this.inputs.forEach(function(input) {
394 inputs.push(input.toObject());
395 });
396 var outputs = [];
397 this.outputs.forEach(function(output) {
398 outputs.push(output.toObject());
399 });
400 var obj = {
401 hash: this.hash,
402 version: this.version,
403 inputs: inputs,
404 outputs: outputs,
405 nLockTime: this.nLockTime
406 };
407 if (this._changeScript) {
408 obj.changeScript = this._changeScript.toString();
409 }
410 if (!_.isUndefined(this._changeIndex)) {
411 obj.changeIndex = this._changeIndex;
412 }
413 if (!_.isUndefined(this._fee)) {
414 obj.fee = this._fee;
415 }
416 return obj;
417};
418
419Transaction.prototype.fromObject = function fromObject(arg, opts) {
420 /* jshint maxstatements: 20 */
421 $.checkArgument(_.isObject(arg) || arg instanceof Transaction);
422 var self = this;
423 var transaction;
424 if (arg instanceof Transaction) {
425 transaction = transaction.toObject();
426 } else {
427 transaction = arg;
428 }
429 _.each(transaction.inputs, function(input) {
430 if (!input.output || !input.output.script) {
431 self.uncheckedAddInput(new Input(input));
432 return;
433 }
434 var script = new Script(input.output.script);
435 var txin;
436 if ((script.isScriptHashOut() || script.isWitnessScriptHashOut()) && input.publicKeys && input.threshold) {
437 txin = new Input.MultiSigScriptHash(
438 input, input.publicKeys, input.threshold, input.signatures, opts
439 );
440 } else if (script.isPublicKeyHashOut() || script.isWitnessPublicKeyHashOut() || script.isScriptHashOut()) {
441 txin = new Input.PublicKeyHash(input);
442 } else if (script.isPublicKeyOut()) {
443 txin = new Input.PublicKey(input);
444 } else {
445 throw new errors.Transaction.Input.UnsupportedScript(input.output.script);
446 }
447 self.addInput(txin);
448 });
449 _.each(transaction.outputs, function(output) {
450 self.addOutput(new Output(output));
451 });
452 if (transaction.changeIndex) {
453 this._changeIndex = transaction.changeIndex;
454 }
455 if (transaction.changeScript) {
456 this._changeScript = new Script(transaction.changeScript);
457 }
458 if (transaction.fee) {
459 this._fee = transaction.fee;
460 }
461 this.nLockTime = transaction.nLockTime;
462 this.version = transaction.version;
463 this._checkConsistency(arg);
464 return this;
465};
466
467Transaction.prototype._checkConsistency = function(arg) {
468 if (!_.isUndefined(this._changeIndex)) {
469 $.checkState(this._changeScript, 'Change script is expected.');
470 $.checkState(this.outputs[this._changeIndex], 'Change index points to undefined output.');
471 $.checkState(this.outputs[this._changeIndex].script.toString() ===
472 this._changeScript.toString(), 'Change output has an unexpected script.');
473 }
474 if (arg && arg.hash) {
475 $.checkState(arg.hash === this.hash, 'Hash in object does not match transaction hash.');
476 }
477};
478
479/**
480 * Sets nLockTime so that transaction is not valid until the desired date(a
481 * timestamp in seconds since UNIX epoch is also accepted)
482 *
483 * @param {Date | Number} time
484 * @return {Transaction} this
485 */
486Transaction.prototype.lockUntilDate = function(time) {
487 $.checkArgument(time);
488 if (_.isNumber(time) && time < Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT) {
489 throw new errors.Transaction.LockTimeTooEarly();
490 }
491 if (_.isDate(time)) {
492 time = time.getTime() / 1000;
493 }
494
495 for (var i = 0; i < this.inputs.length; i++) {
496 if (this.inputs[i].sequenceNumber === Input.DEFAULT_SEQNUMBER){
497 this.inputs[i].sequenceNumber = Input.DEFAULT_LOCKTIME_SEQNUMBER;
498 }
499 }
500
501 this.nLockTime = time;
502 return this;
503};
504
505/**
506 * Sets nLockTime so that transaction is not valid until the desired block
507 * height.
508 *
509 * @param {Number} height
510 * @return {Transaction} this
511 */
512Transaction.prototype.lockUntilBlockHeight = function(height) {
513 $.checkArgument(_.isNumber(height));
514 if (height >= Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT) {
515 throw new errors.Transaction.BlockHeightTooHigh();
516 }
517 if (height < 0) {
518 throw new errors.Transaction.NLockTimeOutOfRange();
519 }
520
521 for (var i = 0; i < this.inputs.length; i++) {
522 if (this.inputs[i].sequenceNumber === Input.DEFAULT_SEQNUMBER){
523 this.inputs[i].sequenceNumber = Input.DEFAULT_LOCKTIME_SEQNUMBER;
524 }
525 }
526
527
528 this.nLockTime = height;
529 return this;
530};
531
532/**
533 * Returns a semantic version of the transaction's nLockTime.
534 * @return {Number|Date}
535 * If nLockTime is 0, it returns null,
536 * if it is < 500000000, it returns a block height (number)
537 * else it returns a Date object.
538 */
539Transaction.prototype.getLockTime = function() {
540 if (!this.nLockTime) {
541 return null;
542 }
543 if (this.nLockTime < Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT) {
544 return this.nLockTime;
545 }
546 return new Date(1000 * this.nLockTime);
547};
548
549Transaction.prototype.fromString = function(string) {
550 this.fromBuffer(buffer.Buffer.from(string, 'hex'));
551};
552
553Transaction.prototype._newTransaction = function() {
554 this.version = CURRENT_VERSION;
555 this.nLockTime = DEFAULT_NLOCKTIME;
556};
557
558/* Transaction creation interface */
559
560/**
561 * @typedef {Object} Transaction~fromObject
562 * @property {string} prevTxId
563 * @property {number} outputIndex
564 * @property {(Buffer|string|Script)} script
565 * @property {number} satoshis
566 */
567
568/**
569 * Add an input to this transaction. This is a high level interface
570 * to add an input, for more control, use @{link Transaction#addInput}.
571 *
572 * Can receive, as output information, the output of bitcoind's `listunspent` command,
573 * and a slightly fancier format recognized by bitcore:
574 *
575 * ```
576 * {
577 * address: 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1',
578 * txId: 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458',
579 * outputIndex: 0,
580 * script: Script.empty(),
581 * satoshis: 1020000
582 * }
583 * ```
584 * Where `address` can be either a string or a bitcore Address object. The
585 * same is true for `script`, which can be a string or a bitcore Script.
586 *
587 * Beware that this resets all the signatures for inputs (in further versions,
588 * SIGHASH_SINGLE or SIGHASH_NONE signatures will not be reset).
589 *
590 * @example
591 * ```javascript
592 * var transaction = new Transaction();
593 *
594 * // From a pay to public key hash output from bitcoind's listunspent
595 * transaction.from({'txid': '0000...', vout: 0, amount: 0.1, scriptPubKey: 'OP_DUP ...'});
596 *
597 * // From a pay to public key hash output
598 * transaction.from({'txId': '0000...', outputIndex: 0, satoshis: 1000, script: 'OP_DUP ...'});
599 *
600 * // From a multisig P2SH output
601 * transaction.from({'txId': '0000...', inputIndex: 0, satoshis: 1000, script: '... OP_HASH'},
602 * ['03000...', '02000...'], 2);
603 * ```
604 *
605 * @param {(Array.<Transaction~fromObject>|Transaction~fromObject)} utxo
606 * @param {Array=} pubkeys
607 * @param {number=} threshold
608 * @param {Object=} opts - Several options:
609 * - noSorting: defaults to false, if true and is multisig, don't
610 * sort the given public keys before creating the script
611 */
612Transaction.prototype.from = function(utxo, pubkeys, threshold, opts) {
613 if (_.isArray(utxo)) {
614 var self = this;
615 _.each(utxo, function(utxo) {
616 self.from(utxo, pubkeys, threshold, opts);
617 });
618 return this;
619 }
620 var exists = _.some(this.inputs, function(input) {
621 // TODO: Maybe prevTxId should be a string? Or defined as read only property?
622 return input.prevTxId.toString('hex') === utxo.txId && input.outputIndex === utxo.outputIndex;
623 });
624 if (exists) {
625 return this;
626 }
627 if (pubkeys && threshold) {
628 this._fromMultisigUtxo(utxo, pubkeys, threshold, opts);
629 } else {
630 this._fromNonP2SH(utxo);
631 }
632 return this;
633};
634
635/**
636 * associateInputs - Update inputs with utxos, allowing you to specify value, and pubkey.
637 * Populating these inputs allows for them to be signed with .sign(privKeys)
638 *
639 * @param {Array<Object>} utxos
640 * @param {Array<string | PublicKey>} pubkeys
641 * @param {number} threshold
642 * @param {Object} opts
643 * @returns {Array<number>}
644 */
645Transaction.prototype.associateInputs = function(utxos, pubkeys, threshold, opts) {
646 let indexes = [];
647 for(let utxo of utxos) {
648 const index = this.inputs.findIndex(i => i.prevTxId.toString('hex') === utxo.txId && i.outputIndex === utxo.outputIndex);
649 indexes.push(index);
650 if(index >= 0) {
651 this.inputs[index] = this._getInputFrom(utxo, pubkeys, threshold, opts);
652 }
653 }
654 return indexes;
655}
656
657
658Transaction.prototype._selectInputType = function(utxo, pubkeys, threshold) {
659 var clazz;
660 utxo = new UnspentOutput(utxo);
661 if(pubkeys && threshold) {
662 if (utxo.script.isMultisigOut()) {
663 clazz = MultiSigInput;
664 } else if (utxo.script.isScriptHashOut() || utxo.script.isWitnessScriptHashOut()) {
665 clazz = MultiSigScriptHashInput;
666 }
667 } else if (utxo.script.isPublicKeyHashOut() || utxo.script.isWitnessPublicKeyHashOut() || utxo.script.isScriptHashOut()) {
668 clazz = PublicKeyHashInput;
669 } else if (utxo.script.isPublicKeyOut()) {
670 clazz = PublicKeyInput;
671 } else {
672 clazz = Input;
673 }
674 return clazz;
675}
676
677
678Transaction.prototype._getInputFrom = function(utxo, pubkeys, threshold, opts) {
679 utxo = new UnspentOutput(utxo);
680 const InputClass = this._selectInputType(utxo, pubkeys, threshold);
681 const input = {
682 output: new Output({
683 script: utxo.script,
684 satoshis: utxo.satoshis
685 }),
686 prevTxId: utxo.txId,
687 outputIndex: utxo.outputIndex,
688 sequenceNumber: utxo.sequenceNumber,
689 script: Script.empty()
690 };
691 let args = pubkeys && threshold ? [pubkeys, threshold, false, opts] : []
692 return new InputClass(input, ...args);
693}
694
695Transaction.prototype._fromNonP2SH = function(utxo) {
696 const input = this._getInputFrom(utxo);
697 this.addInput(input);
698};
699
700Transaction.prototype._fromMultisigUtxo = function(utxo, pubkeys, threshold, opts) {
701 $.checkArgument(threshold <= pubkeys.length,
702 'Number of required signatures must be greater than the number of public keys');
703 const input = this._getInputFrom(utxo, pubkeys, threshold, opts);
704 this.addInput(input);
705};
706
707/**
708 * Add an input to this transaction. The input must be an instance of the `Input` class.
709 * It should have information about the Output that it's spending, but if it's not already
710 * set, two additional parameters, `outputScript` and `satoshis` can be provided.
711 *
712 * @param {Input} input
713 * @param {String|Script} outputScript
714 * @param {number} satoshis
715 * @return Transaction this, for chaining
716 */
717Transaction.prototype.addInput = function(input, outputScript, satoshis) {
718 $.checkArgumentType(input, Input, 'input');
719 if (!input.output && (_.isUndefined(outputScript) || _.isUndefined(satoshis))) {
720 throw new errors.Transaction.NeedMoreInfo('Need information about the UTXO script and satoshis');
721 }
722 if (!input.output && outputScript && !_.isUndefined(satoshis)) {
723 outputScript = outputScript instanceof Script ? outputScript : new Script(outputScript);
724 $.checkArgumentType(satoshis, 'number', 'satoshis');
725 input.output = new Output({
726 script: outputScript,
727 satoshis: satoshis
728 });
729 }
730 return this.uncheckedAddInput(input);
731};
732
733/**
734 * Add an input to this transaction, without checking that the input has information about
735 * the output that it's spending.
736 *
737 * @param {Input} input
738 * @return Transaction this, for chaining
739 */
740Transaction.prototype.uncheckedAddInput = function(input) {
741 $.checkArgumentType(input, Input, 'input');
742 this.inputs.push(input);
743 this._inputAmount = undefined;
744 this._updateChangeOutput();
745 return this;
746};
747
748/**
749 * Returns true if the transaction has enough info on all inputs to be correctly validated
750 *
751 * @return {boolean}
752 */
753Transaction.prototype.hasAllUtxoInfo = function() {
754 return _.every(this.inputs.map(function(input) {
755 return !!input.output;
756 }));
757};
758
759/**
760 * Manually set the fee for this transaction. Beware that this resets all the signatures
761 * for inputs (in further versions, SIGHASH_SINGLE or SIGHASH_NONE signatures will not
762 * be reset).
763 *
764 * @param {number} amount satoshis to be sent
765 * @return {Transaction} this, for chaining
766 */
767Transaction.prototype.fee = function(amount) {
768 $.checkArgument(_.isNumber(amount), 'amount must be a number');
769 this._fee = amount;
770 this._updateChangeOutput();
771 return this;
772};
773
774/**
775 * Manually set the fee per KB for this transaction. Beware that this resets all the signatures
776 * for inputs (in further versions, SIGHASH_SINGLE or SIGHASH_NONE signatures will not
777 * be reset).
778 *
779 * @param {number} amount satoshis per KB to be sent
780 * @return {Transaction} this, for chaining
781 */
782Transaction.prototype.feePerKb = function(amount) {
783 $.checkArgument(_.isNumber(amount), 'amount must be a number');
784 this._feePerKb = amount;
785 this._updateChangeOutput();
786 return this;
787};
788
789/**
790 * Manually set the fee per Byte for this transaction. Beware that this resets all the signatures
791 * for inputs (in further versions, SIGHASH_SINGLE or SIGHASH_NONE signatures will not
792 * be reset).
793 * fee per Byte will be ignored if fee per KB is set
794 *
795 * @param {number} amount satoshis per Byte to be sent
796 * @return {Transaction} this, for chaining
797 */
798Transaction.prototype.feePerByte = function (amount) {
799 $.checkArgument(_.isNumber(amount), 'amount must be a number');
800 this._feePerByte = amount;
801 this._updateChangeOutput();
802 return this;
803};
804
805/* Output management */
806
807/**
808 * Set the change address for this transaction
809 *
810 * Beware that this resets all the signatures for inputs (in further versions,
811 * SIGHASH_SINGLE or SIGHASH_NONE signatures will not be reset).
812 *
813 * @param {Address} address An address for change to be sent to.
814 * @return {Transaction} this, for chaining
815 */
816Transaction.prototype.change = function(address) {
817 $.checkArgument(address, 'address is required');
818 this._changeScript = Script.fromAddress(address);
819 this._updateChangeOutput();
820 return this;
821};
822
823
824/**
825 * @return {Output} change output, if it exists
826 */
827Transaction.prototype.getChangeOutput = function() {
828 if (!_.isUndefined(this._changeIndex)) {
829 return this.outputs[this._changeIndex];
830 }
831 return null;
832};
833
834/**
835 * @typedef {Object} Transaction~toObject
836 * @property {(string|Address)} address
837 * @property {number} satoshis
838 */
839
840/**
841 * Add an output to the transaction.
842 *
843 * Beware that this resets all the signatures for inputs (in further versions,
844 * SIGHASH_SINGLE or SIGHASH_NONE signatures will not be reset).
845 *
846 * @param {(string|Address|Array.<Transaction~toObject>)} address
847 * @param {number} amount in satoshis
848 * @return {Transaction} this, for chaining
849 */
850Transaction.prototype.to = function(address, amount) {
851 if (_.isArray(address)) {
852 var self = this;
853 _.each(address, function(to) {
854 self.to(to.address, to.satoshis);
855 });
856 return this;
857 }
858
859 $.checkArgument(
860 JSUtil.isNaturalNumber(amount),
861 'Amount is expected to be a positive integer'
862 );
863 this.addOutput(new Output({
864 script: Script(new Address(address)),
865 satoshis: amount
866 }));
867 return this;
868};
869
870/**
871 * Add an OP_RETURN output to the transaction.
872 *
873 * Beware that this resets all the signatures for inputs (in further versions,
874 * SIGHASH_SINGLE or SIGHASH_NONE signatures will not be reset).
875 *
876 * @param {Buffer|string} value the data to be stored in the OP_RETURN output.
877 * In case of a string, the UTF-8 representation will be stored
878 * @return {Transaction} this, for chaining
879 */
880Transaction.prototype.addData = function(value) {
881 this.addOutput(new Output({
882 script: Script.buildDataOut(value),
883 satoshis: 0
884 }));
885 return this;
886};
887
888
889/**
890 * Add an output to the transaction.
891 *
892 * @param {Output} output the output to add.
893 * @return {Transaction} this, for chaining
894 */
895Transaction.prototype.addOutput = function(output) {
896 $.checkArgumentType(output, Output, 'output');
897 this._addOutput(output);
898 this._updateChangeOutput();
899 return this;
900};
901
902
903/**
904 * Remove all outputs from the transaction.
905 *
906 * @return {Transaction} this, for chaining
907 */
908Transaction.prototype.clearOutputs = function() {
909 this.outputs = [];
910 this._clearSignatures();
911 this._outputAmount = undefined;
912 this._changeIndex = undefined;
913 this._updateChangeOutput();
914 return this;
915};
916
917
918Transaction.prototype._addOutput = function(output) {
919 this.outputs.push(output);
920 this._outputAmount = undefined;
921};
922
923
924/**
925 * Calculates or gets the total output amount in satoshis
926 *
927 * @return {Number} the transaction total output amount
928 */
929Transaction.prototype._getOutputAmount = function() {
930 if (_.isUndefined(this._outputAmount)) {
931 var self = this;
932 this._outputAmount = 0;
933 _.each(this.outputs, function(output) {
934 self._outputAmount += output.satoshis;
935 });
936 }
937 return this._outputAmount;
938};
939
940
941/**
942 * Calculates or gets the total input amount in satoshis
943 *
944 * @return {Number} the transaction total input amount
945 */
946Transaction.prototype._getInputAmount = function() {
947 if (_.isUndefined(this._inputAmount)) {
948 this._inputAmount = _.sumBy(this.inputs, function(input) {
949 if (_.isUndefined(input.output)) {
950 throw new errors.Transaction.Input.MissingPreviousOutput();
951 }
952 return input.output.satoshis;
953 });
954 }
955 return this._inputAmount;
956};
957
958Transaction.prototype._updateChangeOutput = function() {
959 if (!this._changeScript) {
960 return;
961 }
962 this._clearSignatures();
963 if (!_.isUndefined(this._changeIndex)) {
964 this._removeOutput(this._changeIndex);
965 }
966 var available = this._getUnspentValue();
967 var fee = this.getFee();
968 var changeAmount = available - fee;
969 if (changeAmount > Transaction.DUST_AMOUNT) {
970 this._changeIndex = this.outputs.length;
971 this._addOutput(new Output({
972 script: this._changeScript,
973 satoshis: changeAmount
974 }));
975 } else {
976 this._changeIndex = undefined;
977 }
978};
979/**
980 * Calculates the fee of the transaction.
981 *
982 * If there's a fixed fee set, return that.
983 *
984 * If there is no change output set, the fee is the
985 * total value of the outputs minus inputs. Note that
986 * a serialized transaction only specifies the value
987 * of its outputs. (The value of inputs are recorded
988 * in the previous transaction outputs being spent.)
989 * This method therefore raises a "MissingPreviousOutput"
990 * error when called on a serialized transaction.
991 *
992 * If there's no fee set and no change address,
993 * estimate the fee based on size.
994 *
995 * @return {Number} fee of this transaction in satoshis
996 */
997Transaction.prototype.getFee = function() {
998 if (this.isCoinbase()) {
999 return 0;
1000 }
1001 if (!_.isUndefined(this._fee)) {
1002 return this._fee;
1003 }
1004 // if no change output is set, fees should equal all the unspent amount
1005 if (!this._changeScript) {
1006 return this._getUnspentValue();
1007 }
1008 return this._estimateFee();
1009};
1010
1011/**
1012 * Estimates fee from serialized transaction size in bytes.
1013 */
1014Transaction.prototype._estimateFee = function () {
1015 var estimatedSize = this._estimateSize();
1016 var available = this._getUnspentValue();
1017 var feeRate = this._feePerByte || (this._feePerKb || Transaction.FEE_PER_KB) / 1000;
1018 function getFee(size) {
1019 return size * feeRate;
1020 }
1021 var fee = Math.ceil(getFee(estimatedSize));
1022 var feeWithChange = Math.ceil(getFee(estimatedSize) + getFee(Transaction.CHANGE_OUTPUT_MAX_SIZE));
1023 if (!this._changeScript || available <= feeWithChange) {
1024 return fee;
1025 }
1026 return feeWithChange;
1027};
1028
1029Transaction.prototype._getUnspentValue = function() {
1030 return this._getInputAmount() - this._getOutputAmount();
1031};
1032
1033Transaction.prototype._clearSignatures = function() {
1034 _.each(this.inputs, function(input) {
1035 input.clearSignatures();
1036 });
1037};
1038
1039Transaction.prototype._estimateSize = function() {
1040 var result = Transaction.MAXIMUM_EXTRA_SIZE;
1041 _.each(this.inputs, function(input) {
1042 result += 32 + 4; // prevout size:w
1043 result += input._estimateSize();
1044 });
1045 _.each(this.outputs, function(output) {
1046 result += output.script.toBuffer().length + 9;
1047 });
1048 return Math.ceil(result);
1049};
1050
1051Transaction.prototype._removeOutput = function(index) {
1052 var output = this.outputs[index];
1053 this.outputs = _.without(this.outputs, output);
1054 this._outputAmount = undefined;
1055};
1056
1057Transaction.prototype.removeOutput = function(index) {
1058 this._removeOutput(index);
1059 this._updateChangeOutput();
1060};
1061
1062/**
1063 * Sort a transaction's inputs and outputs according to BIP69
1064 *
1065 * @see {https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki}
1066 * @return {Transaction} this
1067 */
1068Transaction.prototype.sort = function() {
1069 this.sortInputs(function(inputs) {
1070 var copy = Array.prototype.concat.apply([], inputs);
1071 let i = 0;
1072 copy.forEach((x) => { x.i = i++});
1073 copy.sort(function(first, second) {
1074 return compare(first.prevTxId, second.prevTxId)
1075 || first.outputIndex - second.outputIndex
1076 || first.i - second.i; // to ensure stable sort
1077 });
1078 return copy;
1079 });
1080 this.sortOutputs(function(outputs) {
1081 var copy = Array.prototype.concat.apply([], outputs);
1082 let i = 0;
1083 copy.forEach((x) => { x.i = i++});
1084 copy.sort(function(first, second) {
1085 return first.satoshis - second.satoshis
1086 || compare(first.script.toBuffer(), second.script.toBuffer())
1087 || first.i - second.i; // to ensure stable sort
1088 });
1089 return copy;
1090 });
1091 return this;
1092};
1093
1094/**
1095 * Randomize this transaction's outputs ordering. The shuffling algorithm is a
1096 * version of the Fisher-Yates shuffle, provided by lodash's _.shuffle().
1097 *
1098 * @return {Transaction} this
1099 */
1100Transaction.prototype.shuffleOutputs = function() {
1101 return this.sortOutputs(_.shuffle);
1102};
1103
1104/**
1105 * Sort this transaction's outputs, according to a given sorting function that
1106 * takes an array as argument and returns a new array, with the same elements
1107 * but with a different order. The argument function MUST NOT modify the order
1108 * of the original array
1109 *
1110 * @param {Function} sortingFunction
1111 * @return {Transaction} this
1112 */
1113Transaction.prototype.sortOutputs = function(sortingFunction) {
1114 var outs = sortingFunction(this.outputs);
1115 return this._newOutputOrder(outs);
1116};
1117
1118/**
1119 * Sort this transaction's inputs, according to a given sorting function that
1120 * takes an array as argument and returns a new array, with the same elements
1121 * but with a different order.
1122 *
1123 * @param {Function} sortingFunction
1124 * @return {Transaction} this
1125 */
1126Transaction.prototype.sortInputs = function(sortingFunction) {
1127 this.inputs = sortingFunction(this.inputs);
1128 this._clearSignatures();
1129 return this;
1130};
1131
1132Transaction.prototype._newOutputOrder = function(newOutputs) {
1133 var isInvalidSorting = (this.outputs.length !== newOutputs.length ||
1134 _.difference(this.outputs, newOutputs).length !== 0);
1135 if (isInvalidSorting) {
1136 throw new errors.Transaction.InvalidSorting();
1137 }
1138
1139 if (!_.isUndefined(this._changeIndex)) {
1140 var changeOutput = this.outputs[this._changeIndex];
1141 this._changeIndex = _.findIndex(newOutputs, changeOutput);
1142 }
1143
1144 this.outputs = newOutputs;
1145 return this;
1146};
1147
1148Transaction.prototype.removeInput = function(txId, outputIndex) {
1149 var index;
1150 if (!outputIndex && _.isNumber(txId)) {
1151 index = txId;
1152 } else {
1153 index = _.findIndex(this.inputs, function(input) {
1154 return input.prevTxId.toString('hex') === txId && input.outputIndex === outputIndex;
1155 });
1156 }
1157 if (index < 0 || index >= this.inputs.length) {
1158 throw new errors.Transaction.InvalidIndex(index, this.inputs.length);
1159 }
1160 var input = this.inputs[index];
1161 this.inputs = _.without(this.inputs, input);
1162 this._inputAmount = undefined;
1163 this._updateChangeOutput();
1164};
1165
1166/* Signature handling */
1167
1168/**
1169 * Sign the transaction using one or more private keys.
1170 *
1171 * It tries to sign each input, verifying that the signature will be valid
1172 * (matches a public key).
1173 *
1174 * @param {Array|String|PrivateKey} privateKey
1175 * @param {number} sigtype
1176 * @param {String} signingMethod - method used to sign - 'ecdsa' or 'schnorr'
1177 * @return {Transaction} this, for chaining
1178 */
1179Transaction.prototype.sign = function(privateKey, sigtype, signingMethod) {
1180 $.checkState(this.hasAllUtxoInfo(), 'Not all utxo information is available to sign the transaction.');
1181 var self = this;
1182 if (_.isArray(privateKey)) {
1183 _.each(privateKey, function(privateKey) {
1184 self.sign(privateKey, sigtype, signingMethod);
1185 });
1186 return this;
1187 }
1188 _.each(this.getSignatures(privateKey, sigtype, signingMethod), function(signature) {
1189 self.applySignature(signature, signingMethod);
1190 });
1191 return this;
1192};
1193
1194Transaction.prototype.getSignatures = function(privKey, sigtype, signingMethod) {
1195 privKey = new PrivateKey(privKey);
1196 sigtype = sigtype || Signature.SIGHASH_ALL;
1197 var transaction = this;
1198 var results = [];
1199 var hashData = Hash.sha256ripemd160(privKey.publicKey.toBuffer());
1200 _.each(this.inputs, function forEachInput(input, index) {
1201 _.each(input.getSignatures(transaction, privKey, index, sigtype, hashData, signingMethod), function(signature) {
1202 results.push(signature);
1203 });
1204 });
1205 return results;
1206};
1207
1208/**
1209 * Add a signature to the transaction
1210 *
1211 * @param {Object} signature
1212 * @param {number} signature.inputIndex
1213 * @param {number} signature.sigtype
1214 * @param {PublicKey} signature.publicKey
1215 * @param {Signature} signature.signature
1216 * @param {String} signingMethod - 'ecdsa' to sign transaction
1217 * @return {Transaction} this, for chaining
1218 */
1219Transaction.prototype.applySignature = function(signature, signingMethod) {
1220 this.inputs[signature.inputIndex].addSignature(this, signature, signingMethod);
1221 return this;
1222};
1223
1224Transaction.prototype.isFullySigned = function() {
1225 _.each(this.inputs, function(input) {
1226 if (input.isFullySigned === Input.prototype.isFullySigned) {
1227 throw new errors.Transaction.UnableToVerifySignature(
1228 'Unrecognized script kind, or not enough information to execute script.' +
1229 'This usually happens when creating a transaction from a serialized transaction'
1230 );
1231 }
1232 });
1233 return _.every(_.map(this.inputs, function(input) {
1234 return input.isFullySigned();
1235 }));
1236};
1237
1238Transaction.prototype.isValidSignature = function(signature, signingMethod) {
1239 var self = this;
1240 if (this.inputs[signature.inputIndex].isValidSignature === Input.prototype.isValidSignature) {
1241 throw new errors.Transaction.UnableToVerifySignature(
1242 'Unrecognized script kind, or not enough information to execute script.' +
1243 'This usually happens when creating a transaction from a serialized transaction'
1244 );
1245 }
1246 return this.inputs[signature.inputIndex].isValidSignature(self, signature, signingMethod);
1247};
1248
1249/**
1250 * @param {String} signingMethod method used to sign - 'ecdsa' or 'schnorr' (future signing method)
1251 * @returns {bool} whether the signature is valid for this transaction input
1252 */
1253Transaction.prototype.verifySignature = function(sig, pubkey, nin, subscript, sigversion, satoshis, signingMethod) {
1254
1255 if (_.isUndefined(sigversion)) {
1256 sigversion = 0;
1257 }
1258
1259 if (sigversion === 1) {
1260 var subscriptBuffer = subscript.toBuffer();
1261 var scriptCodeWriter = new BufferWriter();
1262 scriptCodeWriter.writeVarintNum(subscriptBuffer.length);
1263 scriptCodeWriter.write(subscriptBuffer);
1264
1265 var satoshisBuffer;
1266 if (satoshis) {
1267 $.checkState(JSUtil.isNaturalNumber(satoshis));
1268 satoshisBuffer = new BufferWriter().writeUInt64LEBN(new BN(satoshis)).toBuffer();
1269 } else {
1270 satoshisBuffer = this.inputs[nin].getSatoshisBuffer();
1271 }
1272 var verified = SighashWitness.verify(
1273 this,
1274 sig,
1275 pubkey,
1276 nin,
1277 scriptCodeWriter.toBuffer(),
1278 satoshisBuffer,
1279 signingMethod
1280 );
1281 return verified;
1282 }
1283
1284 return Sighash.verify(this, sig, pubkey, nin, subscript, signingMethod);
1285};
1286
1287/**
1288 * Check that a transaction passes basic sanity tests. If not, return a string
1289 * describing the error. This function contains the same logic as
1290 * CheckTransaction in bitcoin core.
1291 */
1292Transaction.prototype.verify = function() {
1293 // Basic checks that don't depend on any context
1294 if (this.inputs.length === 0) {
1295 return 'transaction txins empty';
1296 }
1297
1298 if (this.outputs.length === 0) {
1299 return 'transaction txouts empty';
1300 }
1301
1302 // Check for negative or overflow output values
1303 var valueoutbn = new BN(0);
1304 for (var i = 0; i < this.outputs.length; i++) {
1305 var txout = this.outputs[i];
1306
1307 if (txout.invalidSatoshis()) {
1308 return 'transaction txout ' + i + ' satoshis is invalid';
1309 }
1310 if (txout._satoshisBN.gt(new BN(Transaction.MAX_MONEY, 10))) {
1311 return 'transaction txout ' + i + ' greater than MAX_MONEY';
1312 }
1313 valueoutbn = valueoutbn.add(txout._satoshisBN);
1314 if (valueoutbn.gt(new BN(Transaction.MAX_MONEY))) {
1315 return 'transaction txout ' + i + ' total output greater than MAX_MONEY';
1316 }
1317 }
1318
1319 // Size limits
1320 if (this.toBuffer().length > MAX_BLOCK_SIZE) {
1321 return 'transaction over the maximum block size';
1322 }
1323
1324 // Check for duplicate inputs
1325 var txinmap = {};
1326 for (i = 0; i < this.inputs.length; i++) {
1327 var txin = this.inputs[i];
1328
1329 var inputid = txin.prevTxId + ':' + txin.outputIndex;
1330 if (!_.isUndefined(txinmap[inputid])) {
1331 return 'transaction input ' + i + ' duplicate input';
1332 }
1333 txinmap[inputid] = true;
1334 }
1335
1336 var isCoinbase = this.isCoinbase();
1337 if (isCoinbase) {
1338 var buf = this.inputs[0]._scriptBuffer;
1339 if (buf.length < 2 || buf.length > 100) {
1340 return 'coinbase transaction script size invalid';
1341 }
1342 } else {
1343 for (i = 0; i < this.inputs.length; i++) {
1344 if (this.inputs[i].isNull()) {
1345 return 'transaction input ' + i + ' has null input';
1346 }
1347 }
1348 }
1349 return true;
1350};
1351
1352/**
1353 * Analogous to bitcoind's IsCoinBase function in transaction.h
1354 */
1355Transaction.prototype.isCoinbase = function() {
1356 return (this.inputs.length === 1 && this.inputs[0].isNull());
1357};
1358
1359/**
1360 * Determines if this transaction can be replaced in the mempool with another
1361 * transaction that provides a sufficiently higher fee (RBF).
1362 */
1363Transaction.prototype.isRBF = function() {
1364 for (var i = 0; i < this.inputs.length; i++) {
1365 var input = this.inputs[i];
1366 if (input.sequenceNumber < Input.MAXINT - 1) {
1367 return true;
1368 }
1369 }
1370 return false;
1371};
1372
1373/**
1374 * Enable this transaction to be replaced in the mempool (RBF) if a transaction
1375 * includes a sufficiently higher fee. It will set the sequenceNumber to
1376 * DEFAULT_RBF_SEQNUMBER for all inputs if the sequence number does not
1377 * already enable RBF.
1378 */
1379Transaction.prototype.enableRBF = function() {
1380 for (var i = 0; i < this.inputs.length; i++) {
1381 var input = this.inputs[i];
1382 if (input.sequenceNumber >= Input.MAXINT - 1) {
1383 input.sequenceNumber = Input.DEFAULT_RBF_SEQNUMBER;
1384 }
1385 }
1386 return this;
1387};
1388
1389Transaction.prototype.setVersion = function(version) {
1390 $.checkArgument(
1391 JSUtil.isNaturalNumber(version) && version <= CURRENT_VERSION,
1392 'Wrong version number');
1393 this.version = version;
1394 return this;
1395};
1396
1397
1398
1399module.exports = Transaction;