1 | 'use strict';
|
2 |
|
3 | var _ = require('lodash');
|
4 | var $ = require('../util/preconditions');
|
5 | var buffer = require('buffer');
|
6 | var compare = Buffer.compare || require('buffer-compare');
|
7 |
|
8 | var errors = require('../errors');
|
9 | var BufferUtil = require('../util/buffer');
|
10 | var JSUtil = require('../util/js');
|
11 | var BufferReader = require('../encoding/bufferreader');
|
12 | var BufferWriter = require('../encoding/bufferwriter');
|
13 | var Hash = require('../crypto/hash');
|
14 | var Signature = require('../crypto/signature');
|
15 | var Sighash = require('./sighash');
|
16 | var SighashWitness = require('./sighashwitness');
|
17 |
|
18 | var Address = require('../address');
|
19 | var UnspentOutput = require('./unspentoutput');
|
20 | var Input = require('./input');
|
21 | var PublicKeyHashInput = Input.PublicKeyHash;
|
22 | var PublicKeyInput = Input.PublicKey;
|
23 | var MultiSigScriptHashInput = Input.MultiSigScriptHash;
|
24 | var MultiSigInput = Input.MultiSig;
|
25 | var Output = require('./output');
|
26 | var Script = require('../script');
|
27 | var PrivateKey = require('../privatekey');
|
28 | var BN = require('../crypto/bn');
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | function 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 | }
|
61 | var CURRENT_VERSION = 2;
|
62 | var DEFAULT_NLOCKTIME = 0;
|
63 | var MAX_BLOCK_SIZE = 1000000;
|
64 |
|
65 |
|
66 | Transaction.DUST_AMOUNT = 546;
|
67 |
|
68 |
|
69 | Transaction.FEE_SECURITY_MARGIN = 150;
|
70 |
|
71 |
|
72 | Transaction.MAX_MONEY = 21000000 * 1e8;
|
73 |
|
74 |
|
75 | Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT = 5e8;
|
76 |
|
77 |
|
78 | Transaction.NLOCKTIME_MAX_VALUE = 4294967295;
|
79 |
|
80 |
|
81 | Transaction.FEE_PER_KB = 100000;
|
82 |
|
83 |
|
84 | Transaction.CHANGE_OUTPUT_MAX_SIZE = 20 + 4 + 34 + 4;
|
85 | Transaction.MAXIMUM_EXTRA_SIZE = 4 + 9 + 9 + 4;
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 | Transaction.shallowCopy = function(transaction) {
|
97 | var copy = new Transaction(transaction.toBuffer());
|
98 | return copy;
|
99 | };
|
100 |
|
101 | var 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 |
|
110 | var witnessHashProperty = {
|
111 | configurable: false,
|
112 | enumerable: true,
|
113 | get: function() {
|
114 | return new BufferReader(this._getWitnessHash()).readReverse().toString('hex');
|
115 | }
|
116 | };
|
117 |
|
118 | Object.defineProperty(Transaction.prototype, 'witnessHash', witnessHashProperty);
|
119 | Object.defineProperty(Transaction.prototype, 'hash', hashProperty);
|
120 | Object.defineProperty(Transaction.prototype, 'id', hashProperty);
|
121 |
|
122 | var ioProperty = {
|
123 | configurable: false,
|
124 | enumerable: true,
|
125 | get: function() {
|
126 | return this._getInputAmount();
|
127 | }
|
128 | };
|
129 | Object.defineProperty(Transaction.prototype, 'inputAmount', ioProperty);
|
130 | ioProperty.get = function() {
|
131 | return this._getOutputAmount();
|
132 | };
|
133 | Object.defineProperty(Transaction.prototype, 'outputAmount', ioProperty);
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 | Transaction.prototype._getHash = function() {
|
140 | return Hash.sha256sha256(this.toBuffer(true));
|
141 | };
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | Transaction.prototype._getWitnessHash = function() {
|
148 | return Hash.sha256sha256(this.toBuffer(false));
|
149 | };
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | Transaction.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 |
|
173 | Transaction.prototype.uncheckedSerialize = Transaction.prototype.toString = function() {
|
174 | return this.toBuffer().toString('hex');
|
175 | };
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | Transaction.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 |
|
194 | Transaction.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 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 | Transaction.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 |
|
233 | Transaction.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 |
|
265 | Transaction.prototype._missingChange = function() {
|
266 | return !this._changeScript;
|
267 | };
|
268 |
|
269 | Transaction.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 |
|
282 | Transaction.prototype._isMissingSignatures = function(opts) {
|
283 | if (opts.disableIsFullySigned) {
|
284 | return;
|
285 | }
|
286 | if (!this.isFullySigned()) {
|
287 | return new errors.Transaction.MissingSignatures();
|
288 | }
|
289 | };
|
290 |
|
291 | Transaction.prototype.inspect = function() {
|
292 | return '<Transaction: ' + this.uncheckedSerialize() + '>';
|
293 | };
|
294 |
|
295 | Transaction.prototype.toBuffer = function(noWitness) {
|
296 | var writer = new BufferWriter();
|
297 | return this.toBufferWriter(writer, noWitness).toBuffer();
|
298 | };
|
299 |
|
300 | Transaction.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 |
|
309 | Transaction.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 |
|
344 | Transaction.prototype.fromBuffer = function(buffer) {
|
345 | var reader = new BufferReader(buffer);
|
346 | return this.fromBufferReader(reader);
|
347 | };
|
348 |
|
349 | Transaction.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 |
|
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 |
|
391 | Transaction.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 |
|
419 | Transaction.prototype.fromObject = function fromObject(arg, opts) {
|
420 |
|
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 |
|
467 | Transaction.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 |
|
481 |
|
482 |
|
483 |
|
484 |
|
485 |
|
486 | Transaction.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 |
|
507 |
|
508 |
|
509 |
|
510 |
|
511 |
|
512 | Transaction.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 |
|
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 | Transaction.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 |
|
549 | Transaction.prototype.fromString = function(string) {
|
550 | this.fromBuffer(buffer.Buffer.from(string, 'hex'));
|
551 | };
|
552 |
|
553 | Transaction.prototype._newTransaction = function() {
|
554 | this.version = CURRENT_VERSION;
|
555 | this.nLockTime = DEFAULT_NLOCKTIME;
|
556 | };
|
557 |
|
558 |
|
559 |
|
560 |
|
561 |
|
562 |
|
563 |
|
564 |
|
565 |
|
566 |
|
567 |
|
568 |
|
569 |
|
570 |
|
571 |
|
572 |
|
573 |
|
574 |
|
575 |
|
576 |
|
577 |
|
578 |
|
579 |
|
580 |
|
581 |
|
582 |
|
583 |
|
584 |
|
585 |
|
586 |
|
587 |
|
588 |
|
589 |
|
590 |
|
591 |
|
592 |
|
593 |
|
594 |
|
595 |
|
596 |
|
597 |
|
598 |
|
599 |
|
600 |
|
601 |
|
602 |
|
603 |
|
604 |
|
605 |
|
606 |
|
607 |
|
608 |
|
609 |
|
610 |
|
611 |
|
612 | Transaction.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 |
|
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 |
|
637 |
|
638 |
|
639 |
|
640 |
|
641 |
|
642 |
|
643 |
|
644 |
|
645 | Transaction.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 |
|
658 | Transaction.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 |
|
678 | Transaction.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 |
|
695 | Transaction.prototype._fromNonP2SH = function(utxo) {
|
696 | const input = this._getInputFrom(utxo);
|
697 | this.addInput(input);
|
698 | };
|
699 |
|
700 | Transaction.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 |
|
709 |
|
710 |
|
711 |
|
712 |
|
713 |
|
714 |
|
715 |
|
716 |
|
717 | Transaction.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 |
|
735 |
|
736 |
|
737 |
|
738 |
|
739 |
|
740 | Transaction.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 |
|
750 |
|
751 |
|
752 |
|
753 | Transaction.prototype.hasAllUtxoInfo = function() {
|
754 | return _.every(this.inputs.map(function(input) {
|
755 | return !!input.output;
|
756 | }));
|
757 | };
|
758 |
|
759 |
|
760 |
|
761 |
|
762 |
|
763 |
|
764 |
|
765 |
|
766 |
|
767 | Transaction.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 |
|
776 |
|
777 |
|
778 |
|
779 |
|
780 |
|
781 |
|
782 | Transaction.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 |
|
791 |
|
792 |
|
793 |
|
794 |
|
795 |
|
796 |
|
797 |
|
798 | Transaction.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 |
|
806 |
|
807 |
|
808 |
|
809 |
|
810 |
|
811 |
|
812 |
|
813 |
|
814 |
|
815 |
|
816 | Transaction.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 |
|
826 |
|
827 | Transaction.prototype.getChangeOutput = function() {
|
828 | if (!_.isUndefined(this._changeIndex)) {
|
829 | return this.outputs[this._changeIndex];
|
830 | }
|
831 | return null;
|
832 | };
|
833 |
|
834 |
|
835 |
|
836 |
|
837 |
|
838 |
|
839 |
|
840 |
|
841 |
|
842 |
|
843 |
|
844 |
|
845 |
|
846 |
|
847 |
|
848 |
|
849 |
|
850 | Transaction.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 |
|
872 |
|
873 |
|
874 |
|
875 |
|
876 |
|
877 |
|
878 |
|
879 |
|
880 | Transaction.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 |
|
891 |
|
892 |
|
893 |
|
894 |
|
895 | Transaction.prototype.addOutput = function(output) {
|
896 | $.checkArgumentType(output, Output, 'output');
|
897 | this._addOutput(output);
|
898 | this._updateChangeOutput();
|
899 | return this;
|
900 | };
|
901 |
|
902 |
|
903 |
|
904 |
|
905 |
|
906 |
|
907 |
|
908 | Transaction.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 |
|
918 | Transaction.prototype._addOutput = function(output) {
|
919 | this.outputs.push(output);
|
920 | this._outputAmount = undefined;
|
921 | };
|
922 |
|
923 |
|
924 |
|
925 |
|
926 |
|
927 |
|
928 |
|
929 | Transaction.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 |
|
943 |
|
944 |
|
945 |
|
946 | Transaction.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 |
|
958 | Transaction.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 |
|
981 |
|
982 |
|
983 |
|
984 |
|
985 |
|
986 |
|
987 |
|
988 |
|
989 |
|
990 |
|
991 |
|
992 |
|
993 |
|
994 |
|
995 |
|
996 |
|
997 | Transaction.prototype.getFee = function() {
|
998 | if (this.isCoinbase()) {
|
999 | return 0;
|
1000 | }
|
1001 | if (!_.isUndefined(this._fee)) {
|
1002 | return this._fee;
|
1003 | }
|
1004 |
|
1005 | if (!this._changeScript) {
|
1006 | return this._getUnspentValue();
|
1007 | }
|
1008 | return this._estimateFee();
|
1009 | };
|
1010 |
|
1011 |
|
1012 |
|
1013 |
|
1014 | Transaction.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 |
|
1029 | Transaction.prototype._getUnspentValue = function() {
|
1030 | return this._getInputAmount() - this._getOutputAmount();
|
1031 | };
|
1032 |
|
1033 | Transaction.prototype._clearSignatures = function() {
|
1034 | _.each(this.inputs, function(input) {
|
1035 | input.clearSignatures();
|
1036 | });
|
1037 | };
|
1038 |
|
1039 | Transaction.prototype._estimateSize = function() {
|
1040 | var result = Transaction.MAXIMUM_EXTRA_SIZE;
|
1041 | _.each(this.inputs, function(input) {
|
1042 | result += 32 + 4;
|
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 |
|
1051 | Transaction.prototype._removeOutput = function(index) {
|
1052 | var output = this.outputs[index];
|
1053 | this.outputs = _.without(this.outputs, output);
|
1054 | this._outputAmount = undefined;
|
1055 | };
|
1056 |
|
1057 | Transaction.prototype.removeOutput = function(index) {
|
1058 | this._removeOutput(index);
|
1059 | this._updateChangeOutput();
|
1060 | };
|
1061 |
|
1062 |
|
1063 |
|
1064 |
|
1065 |
|
1066 |
|
1067 |
|
1068 | Transaction.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;
|
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;
|
1088 | });
|
1089 | return copy;
|
1090 | });
|
1091 | return this;
|
1092 | };
|
1093 |
|
1094 |
|
1095 |
|
1096 |
|
1097 |
|
1098 |
|
1099 |
|
1100 | Transaction.prototype.shuffleOutputs = function() {
|
1101 | return this.sortOutputs(_.shuffle);
|
1102 | };
|
1103 |
|
1104 |
|
1105 |
|
1106 |
|
1107 |
|
1108 |
|
1109 |
|
1110 |
|
1111 |
|
1112 |
|
1113 | Transaction.prototype.sortOutputs = function(sortingFunction) {
|
1114 | var outs = sortingFunction(this.outputs);
|
1115 | return this._newOutputOrder(outs);
|
1116 | };
|
1117 |
|
1118 |
|
1119 |
|
1120 |
|
1121 |
|
1122 |
|
1123 |
|
1124 |
|
1125 |
|
1126 | Transaction.prototype.sortInputs = function(sortingFunction) {
|
1127 | this.inputs = sortingFunction(this.inputs);
|
1128 | this._clearSignatures();
|
1129 | return this;
|
1130 | };
|
1131 |
|
1132 | Transaction.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 |
|
1148 | Transaction.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 |
|
1167 |
|
1168 |
|
1169 |
|
1170 |
|
1171 |
|
1172 |
|
1173 |
|
1174 |
|
1175 |
|
1176 |
|
1177 |
|
1178 |
|
1179 | Transaction.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 |
|
1194 | Transaction.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 |
|
1210 |
|
1211 |
|
1212 |
|
1213 |
|
1214 |
|
1215 |
|
1216 |
|
1217 |
|
1218 |
|
1219 | Transaction.prototype.applySignature = function(signature, signingMethod) {
|
1220 | this.inputs[signature.inputIndex].addSignature(this, signature, signingMethod);
|
1221 | return this;
|
1222 | };
|
1223 |
|
1224 | Transaction.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 |
|
1238 | Transaction.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 |
|
1251 |
|
1252 |
|
1253 | Transaction.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 |
|
1289 |
|
1290 |
|
1291 |
|
1292 | Transaction.prototype.verify = function() {
|
1293 |
|
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 |
|
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 |
|
1320 | if (this.toBuffer().length > MAX_BLOCK_SIZE) {
|
1321 | return 'transaction over the maximum block size';
|
1322 | }
|
1323 |
|
1324 |
|
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 |
|
1354 |
|
1355 | Transaction.prototype.isCoinbase = function() {
|
1356 | return (this.inputs.length === 1 && this.inputs[0].isNull());
|
1357 | };
|
1358 |
|
1359 |
|
1360 |
|
1361 |
|
1362 |
|
1363 | Transaction.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 |
|
1375 |
|
1376 |
|
1377 |
|
1378 |
|
1379 | Transaction.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 |
|
1389 | Transaction.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 |
|
1399 | module.exports = Transaction;
|