@opcat-labs/opcat 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/.mocharc.yaml +3 -0
  2. package/index.d.ts +1541 -0
  3. package/index.js +74 -0
  4. package/lib/address.js +478 -0
  5. package/lib/block/block.js +277 -0
  6. package/lib/block/blockheader.js +295 -0
  7. package/lib/block/index.js +4 -0
  8. package/lib/block/merkleblock.js +323 -0
  9. package/lib/bn.js +3423 -0
  10. package/lib/crypto/bn.js +278 -0
  11. package/lib/crypto/ecdsa.js +339 -0
  12. package/lib/crypto/hash.browser.js +171 -0
  13. package/lib/crypto/hash.js +2 -0
  14. package/lib/crypto/hash.node.js +171 -0
  15. package/lib/crypto/point.js +221 -0
  16. package/lib/crypto/random.browser.js +28 -0
  17. package/lib/crypto/random.js +2 -0
  18. package/lib/crypto/random.node.js +11 -0
  19. package/lib/crypto/signature.js +325 -0
  20. package/lib/encoding/base58.js +111 -0
  21. package/lib/encoding/base58check.js +121 -0
  22. package/lib/encoding/bufferreader.js +212 -0
  23. package/lib/encoding/bufferwriter.js +140 -0
  24. package/lib/encoding/decode-asm.js +24 -0
  25. package/lib/encoding/decode-hex.js +32 -0
  26. package/lib/encoding/decode-script-chunks.js +43 -0
  27. package/lib/encoding/encode-hex.js +284 -0
  28. package/lib/encoding/is-hex.js +7 -0
  29. package/lib/encoding/varint.js +75 -0
  30. package/lib/errors/index.js +54 -0
  31. package/lib/errors/spec.js +314 -0
  32. package/lib/hash-cache.js +50 -0
  33. package/lib/hdprivatekey.js +678 -0
  34. package/lib/hdpublickey.js +525 -0
  35. package/lib/message/message.js +191 -0
  36. package/lib/mnemonic/mnemonic.js +303 -0
  37. package/lib/mnemonic/pbkdf2.browser.js +68 -0
  38. package/lib/mnemonic/pbkdf2.js +2 -0
  39. package/lib/mnemonic/pbkdf2.node.js +68 -0
  40. package/lib/mnemonic/words/chinese.js +2054 -0
  41. package/lib/mnemonic/words/english.js +2054 -0
  42. package/lib/mnemonic/words/french.js +2054 -0
  43. package/lib/mnemonic/words/index.js +8 -0
  44. package/lib/mnemonic/words/italian.js +2054 -0
  45. package/lib/mnemonic/words/japanese.js +2054 -0
  46. package/lib/mnemonic/words/spanish.js +2054 -0
  47. package/lib/networks.js +379 -0
  48. package/lib/opcode.js +255 -0
  49. package/lib/privatekey.js +374 -0
  50. package/lib/publickey.js +386 -0
  51. package/lib/script/index.js +5 -0
  52. package/lib/script/interpreter.js +1834 -0
  53. package/lib/script/script.js +1074 -0
  54. package/lib/script/stack.js +109 -0
  55. package/lib/script/write-i32-le.js +17 -0
  56. package/lib/script/write-push-data.js +35 -0
  57. package/lib/script/write-u16-le.js +12 -0
  58. package/lib/script/write-u32-le.js +16 -0
  59. package/lib/script/write-u64-le.js +24 -0
  60. package/lib/script/write-u8-le.js +8 -0
  61. package/lib/script/write-varint.js +46 -0
  62. package/lib/transaction/index.js +7 -0
  63. package/lib/transaction/input/index.js +5 -0
  64. package/lib/transaction/input/input.js +354 -0
  65. package/lib/transaction/input/multisig.js +242 -0
  66. package/lib/transaction/input/publickey.js +100 -0
  67. package/lib/transaction/input/publickeyhash.js +118 -0
  68. package/lib/transaction/output.js +231 -0
  69. package/lib/transaction/sighash.js +167 -0
  70. package/lib/transaction/signature.js +97 -0
  71. package/lib/transaction/transaction.js +1639 -0
  72. package/lib/transaction/unspentoutput.js +113 -0
  73. package/lib/util/_.js +47 -0
  74. package/lib/util/js.js +90 -0
  75. package/lib/util/preconditions.js +33 -0
  76. package/package.json +26 -0
  77. package/test/address.js +509 -0
  78. package/test/block/block.js +251 -0
  79. package/test/block/blockheader.js +275 -0
  80. package/test/block/merklebloack.js +211 -0
  81. package/test/crypto/bn.js +177 -0
  82. package/test/crypto/ecdsa.js +391 -0
  83. package/test/crypto/hash.browser.js +135 -0
  84. package/test/crypto/hash.js +136 -0
  85. package/test/crypto/point.js +224 -0
  86. package/test/crypto/random.js +32 -0
  87. package/test/crypto/signature.js +409 -0
  88. package/test/data/bip69.json +215 -0
  89. package/test/data/bitcoind/base58_keys_invalid.json +52 -0
  90. package/test/data/bitcoind/base58_keys_valid.json +335 -0
  91. package/test/data/bitcoind/blocks.json +22 -0
  92. package/test/data/bitcoind/script_tests.json +3822 -0
  93. package/test/data/bitcoind/sig_canonical.json +7 -0
  94. package/test/data/bitcoind/sig_noncanonical.json +36 -0
  95. package/test/data/bitcoind/tx_invalid.json +445 -0
  96. package/test/data/bitcoind/tx_valid.json +44 -0
  97. package/test/data/blk86756-testnet.dat +0 -0
  98. package/test/data/blk86756-testnet.js +14 -0
  99. package/test/data/blk86756-testnet.json +684 -0
  100. package/test/data/block.hex +1 -0
  101. package/test/data/ecdsa.json +230 -0
  102. package/test/data/merkleblocks.js +488 -0
  103. package/test/data/messages.json +22 -0
  104. package/test/data/sighash.json +12 -0
  105. package/test/data/tx_creation.json +95 -0
  106. package/test/encoding/base58.js +131 -0
  107. package/test/encoding/base58check.js +136 -0
  108. package/test/encoding/bufferreader.js +337 -0
  109. package/test/encoding/bufferwriter.js +172 -0
  110. package/test/encoding/varint.js +104 -0
  111. package/test/hashCache.js +67 -0
  112. package/test/hdkeys.js +445 -0
  113. package/test/hdprivatekey.js +332 -0
  114. package/test/hdpublickey.js +304 -0
  115. package/test/index.js +16 -0
  116. package/test/message/message.js +204 -0
  117. package/test/mnemonic/data/fixtures.json +300 -0
  118. package/test/mnemonic/mnemonic.js +259 -0
  119. package/test/mnemonic/mocha.opts +1 -0
  120. package/test/mnemonic/pbkdf2.test.js +59 -0
  121. package/test/networks.js +159 -0
  122. package/test/opcode.js +161 -0
  123. package/test/privatekey.js +439 -0
  124. package/test/publickey.js +554 -0
  125. package/test/script/interpreter.js +734 -0
  126. package/test/script/script.js +1437 -0
  127. package/test/transaction/deserialize.js +34 -0
  128. package/test/transaction/input/input.js +90 -0
  129. package/test/transaction/input/multisig.js +90 -0
  130. package/test/transaction/input/publickey.js +68 -0
  131. package/test/transaction/input/publickeyhash.js +51 -0
  132. package/test/transaction/output.js +185 -0
  133. package/test/transaction/sighash.js +65 -0
  134. package/test/transaction/signature.js +114 -0
  135. package/test/transaction/transaction.js +1109 -0
  136. package/test/transaction/unspentoutput.js +110 -0
  137. package/test/util/js.js +76 -0
  138. package/test/util/preconditions.js +79 -0
@@ -0,0 +1,1639 @@
1
+ 'use strict';
2
+
3
+ var _ = require('../util/_');
4
+ var $ = require('../util/preconditions');
5
+
6
+ var errors = require('../errors');
7
+ var JSUtil = require('../util/js');
8
+ var BufferReader = require('../encoding/bufferreader');
9
+ var BufferWriter = require('../encoding/bufferwriter');
10
+ var Varint = require('../encoding/varint');
11
+ var Hash = require('../crypto/hash');
12
+ var Signature = require('../crypto/signature');
13
+ var Sighash = require('./sighash');
14
+
15
+ var Address = require('../address');
16
+ var UnspentOutput = require('./unspentoutput');
17
+ var Input = require('./input');
18
+ var PublicKeyHashInput = Input.PublicKeyHash;
19
+ var PublicKeyInput = Input.PublicKey;
20
+ var Output = require('./output');
21
+ var Script = require('../script');
22
+ var PrivateKey = require('../privatekey');
23
+ var BN = require('../crypto/bn');
24
+ /**
25
+ * Represents a transaction, a set of inputs and outputs to change ownership of tokens
26
+ *
27
+ * @param {*} serialized
28
+ * @constructor
29
+ */
30
+ function Transaction(serialized) {
31
+ if (!(this instanceof Transaction)) {
32
+ return new Transaction(serialized);
33
+ }
34
+ this.inputs = [];
35
+ this.outputs = [];
36
+ this._inputAmount = undefined;
37
+ this._outputAmount = undefined;
38
+ this._inputsMap = new Map();
39
+ this._outputsMap = new Map();
40
+ this._privateKey = undefined;
41
+ this._sigType = undefined;
42
+ this.sealed = false;
43
+ if (serialized) {
44
+ if (serialized instanceof Transaction) {
45
+ return Transaction.shallowCopy(serialized);
46
+ } else if (JSUtil.isHexa(serialized)) {
47
+ this.fromString(serialized);
48
+ } else if (Buffer.isBuffer(serialized)) {
49
+ this.fromBuffer(serialized);
50
+ } else if (_.isObject(serialized)) {
51
+ this.fromObject(serialized);
52
+ } else {
53
+ throw new errors.InvalidArgument(
54
+ 'Must provide an object or string to deserialize a transaction',
55
+ );
56
+ }
57
+ } else {
58
+ this._newTransaction();
59
+ }
60
+ }
61
+
62
+ var CURRENT_VERSION = 1;
63
+ var DEFAULT_NLOCKTIME = 0;
64
+
65
+ // Minimum amount for an output for it not to be considered a dust output
66
+ Transaction.DUST_AMOUNT = 1;
67
+
68
+ // Margin of error to allow fees in the vecinity of the expected value but doesn't allow a big difference
69
+ Transaction.FEE_SECURITY_MARGIN = 150;
70
+
71
+ // max amount of satoshis in circulation
72
+ Transaction.MAX_MONEY = 21000000 * 1e8;
73
+
74
+ // nlocktime limit to be considered block height rather than a timestamp
75
+ Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT = 5e8;
76
+
77
+ // Max value for an unsigned 32 bit value
78
+ Transaction.NLOCKTIME_MAX_VALUE = 4294967295;
79
+
80
+ // Value used for fee estimation (satoshis per kilobyte)
81
+ Transaction.FEE_PER_KB = 50;
82
+
83
+ // Safe upper bound for change address script size in bytes
84
+ Transaction.CHANGE_OUTPUT_MAX_SIZE = 20 + 4 + 34 + 4;
85
+
86
+ /**
87
+ * a dummy privatekey
88
+ */
89
+ Transaction.DUMMY_PRIVATEKEY = PrivateKey.fromWIF(
90
+ 'cQ3nCBQB9RsFSyjNQM15NQLVpXXMtWh9PUyeFz5KxLJCHsuRH2Su',
91
+ );
92
+
93
+ /* Constructors and Serialization */
94
+
95
+ Transaction.fromString = function (rawHex) {
96
+ return new Transaction().fromString(rawHex)
97
+ }
98
+ Transaction.fromBuffer = function (buffer) {
99
+ return new Transaction().fromBuffer(buffer)
100
+ }
101
+ Transaction.fromObject = function (obj) {
102
+ return new Transaction().fromObject(obj)
103
+ }
104
+
105
+ /**
106
+ * Create a 'shallow' copy of the transaction, by serializing and deserializing
107
+ * it dropping any additional information that inputs and outputs may have hold
108
+ *
109
+ * @param {Transaction} transaction
110
+ * @return {Transaction}
111
+ */
112
+ Transaction.shallowCopy = function (transaction) {
113
+ var copy = new Transaction(transaction.toBuffer());
114
+ return copy;
115
+ };
116
+
117
+ var hashProperty = {
118
+ configurable: false,
119
+ enumerable: true,
120
+ get: function () {
121
+ this._hash = new BufferReader(this._getHash()).readReverse().toString('hex');
122
+ return this._hash;
123
+ },
124
+ };
125
+ Object.defineProperty(Transaction.prototype, 'hash', hashProperty);
126
+ Object.defineProperty(Transaction.prototype, 'id', hashProperty);
127
+
128
+ var ioProperty = {
129
+ configurable: false,
130
+ enumerable: true,
131
+ get: function () {
132
+ return this._getInputAmount();
133
+ },
134
+ };
135
+ Object.defineProperty(Transaction.prototype, 'inputAmount', ioProperty);
136
+ ioProperty.get = function () {
137
+ return this._getOutputAmount();
138
+ };
139
+ Object.defineProperty(Transaction.prototype, 'outputAmount', ioProperty);
140
+
141
+ /**
142
+ * Retrieve the little endian hash of the transaction (used for serialization)
143
+ * @return {Buffer}
144
+ */
145
+ Transaction.prototype._getHash = function () {
146
+ return Hash.sha256sha256(this.toBufferWriter(true).toBuffer());
147
+ };
148
+
149
+ /**
150
+ * Retrieve a hexa string that can be used with bitcoind's CLI interface
151
+ * (decoderawtransaction, sendrawtransaction)
152
+ *
153
+ * @param {Object|boolean=} unsafe if true, skip all tests. if it's an object,
154
+ * it's expected to contain a set of flags to skip certain tests:
155
+ * * `disableAll`: disable all checks
156
+ * * `disableLargeFees`: disable checking for fees that are too large
157
+ * * `disableIsFullySigned`: disable checking if all inputs are fully signed
158
+ * * `disableDustOutputs`: disable checking if there are no outputs that are dust amounts
159
+ * * `disableMoreOutputThanInput`: disable checking if the transaction spends more bitcoins than the sum of the input amounts
160
+ * @return {string}
161
+ */
162
+ Transaction.prototype.serialize = function (unsafe) {
163
+ if (unsafe === true || (unsafe && unsafe.disableAll)) {
164
+ return this.uncheckedSerialize();
165
+ } else {
166
+ return this.checkedSerialize(unsafe);
167
+ }
168
+ };
169
+
170
+ Transaction.prototype.clone = function () {
171
+ const tx = Transaction.fromString(this.uncheckedSerialize());
172
+ this.inputs.forEach((input, index) => {
173
+ if(input.output) {
174
+ tx.inputs[index].output = input.output.clone();
175
+ }
176
+ });
177
+ return tx;
178
+ };
179
+
180
+ Transaction.prototype.uncheckedSerialize = Transaction.prototype.toString = Transaction.prototype.toHex = function () {
181
+ return this.toBuffer().toString('hex');
182
+ };
183
+
184
+ /**
185
+ * Retrieve a hexa string that can be used with bitcoind's CLI interface
186
+ * (decoderawtransaction, sendrawtransaction)
187
+ *
188
+ * @param {Object} opts allows to skip certain tests. {@see Transaction#serialize}
189
+ * @return {string}
190
+ */
191
+ Transaction.prototype.checkedSerialize = function (opts) {
192
+ var serializationError = this.getSerializationError(opts);
193
+ if (serializationError) {
194
+ serializationError.message +=
195
+ ' - For more information please see: ' + 'https://github.com/OPCAT-Labs/ts-tools';
196
+ throw serializationError;
197
+ }
198
+ return this.uncheckedSerialize();
199
+ };
200
+
201
+ Transaction.prototype.invalidSatoshis = function () {
202
+ var invalid = false;
203
+ for (var i = 0; i < this.outputs.length; i++) {
204
+ if (this.outputs[i].invalidSatoshis()) {
205
+ invalid = true;
206
+ }
207
+ }
208
+ return invalid;
209
+ };
210
+
211
+ /**
212
+ * Retrieve a possible error that could appear when trying to serialize and
213
+ * broadcast this transaction.
214
+ *
215
+ * @param {Object} opts allows to skip certain tests. {@see Transaction#serialize}
216
+ * @return {opcat.Error}
217
+ */
218
+ Transaction.prototype.getSerializationError = function (opts) {
219
+ opts = opts || {};
220
+
221
+ if (this.invalidSatoshis()) {
222
+ return new errors.Transaction.InvalidSatoshis();
223
+ }
224
+
225
+ var unspent = this.getUnspentValue();
226
+ var unspentError;
227
+ if (unspent < 0) {
228
+ if (!opts.disableMoreOutputThanInput) {
229
+ unspentError = new errors.Transaction.InvalidOutputAmountSum();
230
+ }
231
+ } else {
232
+ unspentError = this._hasFeeError(opts, unspent);
233
+ }
234
+
235
+ return unspentError || this._hasDustOutputs(opts) || this._isMissingSignatures(opts);
236
+ };
237
+
238
+ Transaction.prototype._hasFeeError = function (opts, unspent) {
239
+ if (!_.isUndefined(this._fee) && this._fee !== unspent) {
240
+ return new errors.Transaction.FeeError.Different(
241
+ 'Unspent value is ' + unspent + ' but specified fee is ' + this._fee,
242
+ );
243
+ }
244
+
245
+ if (!opts.disableLargeFees) {
246
+ var maximumFee = Math.floor(Transaction.FEE_SECURITY_MARGIN * this._estimateFee());
247
+ if (unspent > maximumFee) {
248
+ if (this._missingChange()) {
249
+ return new errors.Transaction.ChangeAddressMissing(
250
+ 'Fee is too large and no change address was provided',
251
+ );
252
+ }
253
+ return new errors.Transaction.FeeError.TooLarge(
254
+ 'expected less than ' + maximumFee + ' but got ' + unspent,
255
+ );
256
+ }
257
+ }
258
+ };
259
+
260
+ Transaction.prototype._missingChange = function () {
261
+ return !this._changeScript;
262
+ };
263
+
264
+ Transaction.prototype._hasDustOutputs = function (opts) {
265
+ if (opts.disableDustOutputs) {
266
+ return;
267
+ }
268
+ var index, output;
269
+ for (index in this.outputs) {
270
+ output = this.outputs[index];
271
+ if (
272
+ output.satoshis < Transaction.DUST_AMOUNT &&
273
+ !output.script.isDataOut() &&
274
+ !output.script.isSafeDataOut()
275
+ ) {
276
+ return new errors.Transaction.DustOutputs();
277
+ }
278
+ }
279
+ };
280
+
281
+ Transaction.prototype._isMissingSignatures = function (opts) {
282
+ if (opts.disableIsFullySigned) {
283
+ return;
284
+ }
285
+ if (!this.isFullySigned()) {
286
+ return new errors.Transaction.MissingSignatures();
287
+ }
288
+ };
289
+
290
+ Transaction.prototype.inspect = function () {
291
+ return '<Transaction: ' + this.uncheckedSerialize() + '>';
292
+ };
293
+
294
+ Transaction.prototype.toBuffer = function () {
295
+ var writer = new BufferWriter();
296
+ return this.toBufferWriter(false, writer).toBuffer();
297
+ };
298
+
299
+ Transaction.prototype.hashForSignature = function (inputIndex, hashType) {
300
+ var preimage = this.getPreimage(inputIndex, hashType);
301
+ var ret = Hash.sha256sha256(preimage)
302
+ return new BufferReader(ret).readReverse()
303
+ }
304
+
305
+ Transaction.prototype.toTxHashPreimage = function () {
306
+ var writer = new BufferWriter();
307
+ return this.toBufferWriter(true, writer).toBuffer();
308
+ }
309
+
310
+
311
+ Transaction.prototype.toBufferWriter = function (forTxHash, writer) {
312
+ $.checkArgument(typeof forTxHash === 'boolean', 'forTxHash parameter must be a boolean');
313
+ writer = writer || new BufferWriter();
314
+ writer.writeUInt32LE(this.version);
315
+ writer.writeVarintNum(this.inputs.length);
316
+ _.each(this.inputs, function (input) {
317
+ input.toBufferWriter(forTxHash, writer);
318
+ });
319
+ writer.writeVarintNum(this.outputs.length);
320
+ _.each(this.outputs, function (output) {
321
+ output.toBufferWriter(forTxHash, writer);
322
+ });
323
+ writer.writeUInt32LE(this.nLockTime);
324
+ return writer;
325
+ };
326
+
327
+ Transaction.prototype.fromBuffer = function (buffer) {
328
+ var reader = new BufferReader(buffer);
329
+ return this.fromBufferReader(reader);
330
+ };
331
+
332
+ Transaction.prototype.fromBufferReader = function (reader) {
333
+ $.checkArgument(!reader.finished(), 'No transaction data received');
334
+ var i, sizeTxIns, sizeTxOuts;
335
+
336
+ this.version = reader.readInt32LE();
337
+ sizeTxIns = reader.readVarintNum();
338
+ for (i = 0; i < sizeTxIns; i++) {
339
+ var input = Input.fromBufferReader(reader);
340
+ this.inputs.push(input);
341
+ }
342
+ sizeTxOuts = reader.readVarintNum();
343
+ for (i = 0; i < sizeTxOuts; i++) {
344
+ this.outputs.push(Output.fromBufferReader(reader));
345
+ }
346
+ this.nLockTime = reader.readUInt32LE();
347
+ return this;
348
+ };
349
+
350
+ Transaction.prototype.toObject = Transaction.prototype.toJSON = function toObject() {
351
+ var inputs = [];
352
+ this.inputs.forEach(function (input) {
353
+ inputs.push(input.toObject());
354
+ });
355
+ var outputs = [];
356
+ this.outputs.forEach(function (output) {
357
+ outputs.push(output.toObject());
358
+ });
359
+ var obj = {
360
+ hash: this.hash,
361
+ version: this.version,
362
+ inputs: inputs,
363
+ outputs: outputs,
364
+ nLockTime: this.nLockTime,
365
+ };
366
+ if (this._changeScript) {
367
+ obj.changeScript = this._changeScript.toString();
368
+ }
369
+
370
+ if (this._changeScript) {
371
+ obj.changeAddress = this._changeAddress.toString();
372
+ }
373
+
374
+ if (!_.isUndefined(this._changeIndex)) {
375
+ obj.changeIndex = this._changeIndex;
376
+ }
377
+ if (!_.isUndefined(this._fee)) {
378
+ obj.fee = this._fee;
379
+ }
380
+ return obj;
381
+ };
382
+
383
+ Transaction.prototype.fromObject = function fromObject(arg) {
384
+ $.checkArgument(_.isObject(arg) || arg instanceof Transaction);
385
+ var self = this;
386
+ var transaction;
387
+ if (arg instanceof Transaction) {
388
+ transaction = transaction.toObject();
389
+ } else {
390
+ transaction = arg;
391
+ }
392
+ _.each(transaction.inputs, function (input) {
393
+ if (!input.output || !input.output.script) {
394
+ self.uncheckedAddInput(new Input(input));
395
+ return;
396
+ }
397
+ var script = new Script(input.output.script);
398
+ var txin;
399
+ if (script.isPublicKeyHashOut()) {
400
+ txin = new Input.PublicKeyHash(input);
401
+ } else if (script.isPublicKeyOut()) {
402
+ txin = new Input.PublicKey(input);
403
+ } else {
404
+ txin = new Input(input)
405
+ }
406
+ self.addInput(txin);
407
+ });
408
+ _.each(transaction.outputs, function (output) {
409
+ self.addOutput(new Output(output));
410
+ });
411
+ if (transaction.changeIndex) {
412
+ this._changeIndex = transaction.changeIndex;
413
+ }
414
+ if (transaction.changeScript) {
415
+ this._changeScript = new Script(transaction.changeScript);
416
+ }
417
+ if (transaction.changeAddress) {
418
+ this._changeAddress = Address.fromString(transaction.changeAddress);
419
+ }
420
+ if (transaction.changeData) {
421
+ this._changeData = transaction.changeData;
422
+ }
423
+ if (transaction.fee) {
424
+ this._fee = transaction.fee;
425
+ }
426
+ this.nLockTime = transaction.nLockTime;
427
+ this.version = transaction.version;
428
+ this._checkConsistency(arg);
429
+ return this;
430
+ };
431
+
432
+ Transaction.prototype._checkConsistency = function (arg) {
433
+ if (!_.isUndefined(this._changeIndex)) {
434
+ $.checkState(this._changeScript, 'Change script is expected.');
435
+ $.checkState(this._changeAddress, 'Change address is expected.');
436
+ $.checkState(this.outputs[this._changeIndex], 'Change index points to undefined output.');
437
+ $.checkState(
438
+ this.outputs[this._changeIndex].script.toString() === this._changeScript.toString(),
439
+ 'Change output has an unexpected script.',
440
+ );
441
+ }
442
+ if (arg && arg.hash) {
443
+ $.checkState(arg.hash === this.hash, 'Hash in object does not match transaction hash.');
444
+ }
445
+ };
446
+
447
+ /**
448
+ * Sets nLockTime so that transaction is not valid until the desired date(a
449
+ * timestamp in seconds since UNIX epoch is also accepted)
450
+ *
451
+ * @param {Date | Number} time
452
+ * @return {Transaction} this
453
+ */
454
+ Transaction.prototype.lockUntilDate = function (time) {
455
+ $.checkArgument(time);
456
+ if (_.isNumber(time) && time < Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT) {
457
+ throw new errors.Transaction.LockTimeTooEarly();
458
+ }
459
+ if (_.isDate(time)) {
460
+ time = time.getTime() / 1000;
461
+ }
462
+
463
+ for (var i = 0; i < this.inputs.length; i++) {
464
+ if (this.inputs[i].sequenceNumber === Input.DEFAULT_SEQNUMBER) {
465
+ this.inputs[i].sequenceNumber = Input.DEFAULT_LOCKTIME_SEQNUMBER;
466
+ }
467
+ }
468
+
469
+ this.nLockTime = time;
470
+ return this;
471
+ };
472
+
473
+ /**
474
+ * Sets nLockTime so that transaction is not valid until the desired block
475
+ * height.
476
+ *
477
+ * @param {Number} height
478
+ * @return {Transaction} this
479
+ */
480
+ Transaction.prototype.lockUntilBlockHeight = function (height) {
481
+ $.checkArgument(_.isNumber(height));
482
+ if (height >= Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT) {
483
+ throw new errors.Transaction.BlockHeightTooHigh();
484
+ }
485
+ if (height < 0) {
486
+ throw new errors.Transaction.NLockTimeOutOfRange();
487
+ }
488
+
489
+ for (var i = 0; i < this.inputs.length; i++) {
490
+ if (this.inputs[i].sequenceNumber === Input.DEFAULT_SEQNUMBER) {
491
+ this.inputs[i].sequenceNumber = Input.DEFAULT_LOCKTIME_SEQNUMBER;
492
+ }
493
+ }
494
+
495
+ this.nLockTime = height;
496
+ return this;
497
+ };
498
+
499
+ /**
500
+ * Returns a semantic version of the transaction's nLockTime.
501
+ * @return {Number|Date}
502
+ * If nLockTime is 0, it returns null,
503
+ * if it is < 500000000, it returns a block height (number)
504
+ * else it returns a Date object.
505
+ */
506
+ Transaction.prototype.getLockTime = function () {
507
+ if (!this.nLockTime) {
508
+ return null;
509
+ }
510
+ if (this.nLockTime < Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT) {
511
+ return this.nLockTime;
512
+ }
513
+ return new Date(1000 * this.nLockTime);
514
+ };
515
+
516
+ Transaction.prototype.fromString = function (string) {
517
+ return this.fromBuffer(Buffer.from(string, 'hex'));
518
+ };
519
+
520
+ Transaction.prototype._newTransaction = function () {
521
+ this.version = CURRENT_VERSION;
522
+ this.nLockTime = DEFAULT_NLOCKTIME;
523
+ };
524
+
525
+ /* Transaction creation interface */
526
+
527
+ /**
528
+ * @typedef {Object} Transaction~fromObject
529
+ * @property {string} prevTxId
530
+ * @property {number} outputIndex
531
+ * @property {(Buffer|string|Script)} script
532
+ * @property {number} satoshis
533
+ */
534
+
535
+ /**
536
+ * Add an input to this transaction. This is a high level interface
537
+ * to add an input, for more control, use @{link Transaction#addInput}.
538
+ *
539
+ * Can receive, as output information, the output of bitcoind's `listunspent` command,
540
+ * and a slightly fancier format recognized by opcat:
541
+ *
542
+ * ```
543
+ * {
544
+ * address: 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1',
545
+ * txId: 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458',
546
+ * outputIndex: 0,
547
+ * script: Script.empty(),
548
+ * satoshis: 1020000
549
+ * }
550
+ * ```
551
+ * Where `address` can be either a string or a opcat Address object. The
552
+ * same is true for `script`, which can be a string or a opcat Script.
553
+ *
554
+ * Beware that this resets all the signatures for inputs (in further versions,
555
+ * SIGHASH_SINGLE or SIGHASH_NONE signatures will not be reset).
556
+ *
557
+ * @example
558
+ * ```javascript
559
+ * var transaction = new Transaction();
560
+ *
561
+ * // From a pay to public key hash output from bitcoind's listunspent
562
+ * transaction.from({'txid': '0000...', vout: 0, amount: 0.1, scriptPubKey: 'OP_DUP ...'});
563
+ *
564
+ * // From a pay to public key hash output
565
+ * transaction.from({'txId': '0000...', outputIndex: 0, satoshis: 1000, script: 'OP_DUP ...'});
566
+ *
567
+ * ```
568
+ *
569
+ * @param {(Array.<Transaction~fromObject>|Transaction~fromObject)} utxo
570
+ * @param {Array=} pubkeys
571
+ * @param {number=} threshold
572
+ */
573
+ Transaction.prototype.from = function (utxo) {
574
+ if (_.isArray(utxo)) {
575
+ var self = this;
576
+ _.each(utxo, function (utxo) {
577
+ self.from(utxo);
578
+ });
579
+ return this;
580
+ }
581
+ var exists = _.some(this.inputs, function (input) {
582
+ // TODO: Maybe prevTxId should be a string? Or defined as read only property?
583
+ return input.prevTxId.toString('hex') === utxo.txId && input.outputIndex === utxo.outputIndex;
584
+ });
585
+ if (exists) {
586
+ return this;
587
+ }
588
+ this._fromUTXO(utxo);
589
+ return this;
590
+ };
591
+
592
+ Transaction.prototype._fromUTXO = function (utxo) {
593
+ var Clazz;
594
+ utxo = new UnspentOutput(utxo);
595
+ if (utxo.script.isPublicKeyHashOut()) {
596
+ Clazz = PublicKeyHashInput;
597
+ } else if (utxo.script.isPublicKeyOut()) {
598
+ Clazz = PublicKeyInput;
599
+ } else {
600
+ Clazz = Input;
601
+ }
602
+ this.addInput(
603
+ new Clazz({
604
+ output: new Output({
605
+ script: utxo.script,
606
+ satoshis: utxo.satoshis,
607
+ data: utxo.data || Buffer.from([]),
608
+ }),
609
+ prevTxId: utxo.txId,
610
+ outputIndex: utxo.outputIndex,
611
+ script: Script.empty(),
612
+ }),
613
+ );
614
+ };
615
+
616
+ /**
617
+ * Add an input to this transaction. The input must be an instance of the `Input` class.
618
+ * It should have information about the Output that it's spending, but if it's not already
619
+ * set, two additional parameters, `outputScript` and `satoshis` can be provided.
620
+ *
621
+ * @param {Input} input
622
+ * @param {String|Script} outputScript
623
+ * @param {number} satoshis
624
+ * @return Transaction this, for chaining
625
+ */
626
+ Transaction.prototype.addInput = function (input, outputScript, satoshis, data) {
627
+ $.checkArgumentType(input, Input, 'input');
628
+ if (!input.output && (_.isUndefined(outputScript) || _.isUndefined(satoshis))) {
629
+ throw new errors.Transaction.NeedMoreInfo(
630
+ 'Need information about the UTXO script and satoshis',
631
+ );
632
+ }
633
+ if (!input.output && outputScript && !_.isUndefined(satoshis)) {
634
+ outputScript = outputScript instanceof Script ? outputScript : new Script(outputScript);
635
+ $.checkArgumentType(satoshis, 'number', 'satoshis');
636
+
637
+ input.output = new Output({
638
+ script: outputScript,
639
+ satoshis: satoshis,
640
+ data: data,
641
+ });
642
+ }
643
+ return this.uncheckedAddInput(input);
644
+ };
645
+
646
+ /**
647
+ * Add an input to this transaction, without checking that the input has information about
648
+ * the output that it's spending.
649
+ *
650
+ * @param {Input} input
651
+ * @return Transaction this, for chaining
652
+ */
653
+ Transaction.prototype.uncheckedAddInput = function (input) {
654
+ $.checkArgumentType(input, Input, 'input');
655
+ this.inputs.push(input);
656
+ this._inputAmount = undefined;
657
+ this._updateChangeOutput();
658
+ return this;
659
+ };
660
+
661
+ /**
662
+ * Returns true if the transaction has enough info on all inputs to be correctly validated
663
+ *
664
+ * @return {boolean}
665
+ */
666
+ Transaction.prototype.hasAllUtxoInfo = function () {
667
+ return _.every(
668
+ this.inputs.map(function (input) {
669
+ return !!input.output;
670
+ }),
671
+ );
672
+ };
673
+
674
+ /**
675
+ * Manually set the fee for this transaction. Beware that this resets all the signatures
676
+ * for inputs (in further versions, SIGHASH_SINGLE or SIGHASH_NONE signatures will not
677
+ * be reset).
678
+ *
679
+ * @param {number} amount satoshis to be sent
680
+ * @return {Transaction} this, for chaining
681
+ */
682
+ Transaction.prototype.fee = function (amount) {
683
+ $.checkArgument(_.isNumber(amount), 'amount must be a number');
684
+ this._fee = amount;
685
+ this._updateChangeOutput();
686
+ return this;
687
+ };
688
+
689
+ /**
690
+ * Manually set the fee per KB for this transaction. Beware that this resets all the signatures
691
+ * for inputs (in further versions, SIGHASH_SINGLE or SIGHASH_NONE signatures will not
692
+ * be reset).
693
+ *
694
+ * @param {number} amount satoshis per KB to be sent
695
+ * @return {Transaction} this, for chaining
696
+ */
697
+ Transaction.prototype.feePerKb = function (amount) {
698
+ $.checkArgument(_.isNumber(amount), 'amount must be a number');
699
+ this._feePerKb = amount;
700
+ this._updateChangeOutput();
701
+ return this;
702
+ };
703
+
704
+ /* Output management */
705
+
706
+ /**
707
+ * Set the change address for this transaction
708
+ *
709
+ * Beware that this resets all the signatures for inputs (in further versions,
710
+ * SIGHASH_SINGLE or SIGHASH_NONE signatures will not be reset).
711
+ *
712
+ * @param {Address} address An address for change to be sent to.
713
+ * @param {Buffer|string} data The data to be stored in the change output.
714
+ * @return {Transaction} this, for chaining
715
+ */
716
+ Transaction.prototype.change = function (address, data) {
717
+ $.checkArgument(address, 'address is required');
718
+ this._changeScript = Script.fromAddress(address);
719
+ this._changeAddress = Address(address);
720
+ if (data) {
721
+ this._changeData = data
722
+ }
723
+ this._updateChangeOutput();
724
+ return this;
725
+ };
726
+
727
+ /**
728
+ * @return {Output} change output, if it exists
729
+ */
730
+ Transaction.prototype.getChangeOutput = function () {
731
+ if (!_.isUndefined(this._changeIndex)) {
732
+ return this.outputs[this._changeIndex];
733
+ }
734
+ return null;
735
+ };
736
+
737
+ /**
738
+ * @return {Address | null} change address, if it exists
739
+ */
740
+ Transaction.prototype.getChangeAddress = function () {
741
+ return this._changeAddress ? this._changeAddress : null;
742
+ };
743
+
744
+ /**
745
+ * @typedef {Object} Transaction~toObject
746
+ * @property {(string|Address)} address
747
+ * @property {number} satoshis
748
+ */
749
+
750
+ /**
751
+ * Add an output to the transaction.
752
+ *
753
+ * Beware that this resets all the signatures for inputs (in further versions,
754
+ * SIGHASH_SINGLE or SIGHASH_NONE signatures will not be reset).
755
+ *
756
+ * @param {(string|Address|Array.<Transaction~toObject>)} address
757
+ * @param {number} amount in satoshis
758
+ * @return {Transaction} this, for chaining
759
+ */
760
+ Transaction.prototype.to = function (address, amount) {
761
+ if (_.isArray(address)) {
762
+ var self = this;
763
+ _.each(address, function (to) {
764
+ self.to(to.address, to.satoshis);
765
+ });
766
+ return this;
767
+ }
768
+
769
+ $.checkArgument(JSUtil.isNaturalNumber(amount), 'Amount is expected to be a positive integer');
770
+ this.addOutput(
771
+ new Output({
772
+ script: Script(new Address(address)),
773
+ satoshis: amount,
774
+ }),
775
+ );
776
+ return this;
777
+ };
778
+
779
+ /**
780
+ * Add an OP_RETURN output to the transaction.
781
+ *
782
+ * Beware that this resets all the signatures for inputs (in further versions,
783
+ * SIGHASH_SINGLE or SIGHASH_NONE signatures will not be reset).
784
+ *
785
+ * @param {Buffer|string} value the data to be stored in the OP_RETURN output.
786
+ * In case of a string, the UTF-8 representation will be stored
787
+ * @return {Transaction} this, for chaining
788
+ */
789
+ Transaction.prototype.addData = function (value) {
790
+ this.addOutput(
791
+ new Output({
792
+ script: Script.buildDataOut(value),
793
+ satoshis: 0,
794
+ }),
795
+ );
796
+ return this;
797
+ };
798
+
799
+ /**
800
+ * Add an OP_FALSE | OP_RETURN output to the transaction.
801
+ *
802
+ * Beware that this resets all the signatures for inputs (in further versions,
803
+ * SIGHASH_SINGLE or SIGHASH_NONE signatures will not be reset).
804
+ *
805
+ * @param {Buffer|string} value the data to be stored in the OP_RETURN output.
806
+ * In case of a string, the UTF-8 representation will be stored
807
+ * @return {Transaction} this, for chaining
808
+ */
809
+ Transaction.prototype.addSafeData = function (value) {
810
+ this.addOutput(
811
+ new Output({
812
+ script: Script.buildSafeDataOut(value),
813
+ satoshis: 0,
814
+ }),
815
+ );
816
+ return this;
817
+ };
818
+
819
+ /**
820
+ * Add an output to the transaction.
821
+ *
822
+ * @param {Output} output the output to add.
823
+ * @return {Transaction} this, for chaining
824
+ */
825
+ Transaction.prototype.addOutput = function (output) {
826
+ $.checkArgumentType(output, Output, 'output');
827
+ this._addOutput(output);
828
+ this._updateChangeOutput();
829
+ return this;
830
+ };
831
+
832
+ /**
833
+ * Remove all outputs from the transaction.
834
+ *
835
+ * @return {Transaction} this, for chaining
836
+ */
837
+ Transaction.prototype.clearOutputs = function () {
838
+ this.outputs = [];
839
+ this._clearSignatures();
840
+ this._outputAmount = undefined;
841
+ this._changeIndex = undefined;
842
+ this._updateChangeOutput();
843
+ return this;
844
+ };
845
+
846
+ Transaction.prototype._addOutput = function (output) {
847
+ this.outputs.push(output);
848
+ this._outputAmount = undefined;
849
+ };
850
+
851
+ /**
852
+ * Calculates or gets the total output amount in satoshis
853
+ *
854
+ * @return {Number} the transaction total output amount
855
+ */
856
+ Transaction.prototype._getOutputAmount = function () {
857
+ if (_.isUndefined(this._outputAmount)) {
858
+ var self = this;
859
+ this._outputAmount = 0;
860
+ _.each(this.outputs, function (output) {
861
+ self._outputAmount += output.satoshis;
862
+ });
863
+ }
864
+ return this._outputAmount;
865
+ };
866
+
867
+ /**
868
+ * Calculates or gets the total input amount in satoshis
869
+ *
870
+ * @return {Number} the transaction total input amount
871
+ */
872
+ Transaction.prototype._getInputAmount = function () {
873
+ if (_.isUndefined(this._inputAmount)) {
874
+ var self = this;
875
+ this._inputAmount = 0;
876
+ _.each(this.inputs, function (input) {
877
+ if (_.isUndefined(input.output)) {
878
+ throw new errors.Transaction.Input.MissingPreviousOutput();
879
+ }
880
+ self._inputAmount += input.output.satoshis;
881
+ });
882
+ }
883
+ return this._inputAmount;
884
+ };
885
+
886
+ Transaction.prototype._updateChangeOutput = function () {
887
+ if (this.sealed) {
888
+ throw new errors.Transaction.TransactionAlreadySealed();
889
+ }
890
+
891
+ if (!this._changeScript) {
892
+ return;
893
+ }
894
+ if (!_.isUndefined(this._changeIndex)) {
895
+ this._removeOutput(this._changeIndex);
896
+ }
897
+ this._changeIndex = this.outputs.length;
898
+ this._addOutput(
899
+ new Output({
900
+ script: this._changeScript,
901
+ satoshis: 0,
902
+ data: this._changeData || ''
903
+ }),
904
+ );
905
+ var available = this.getUnspentValue();
906
+ var fee = this.getFee();
907
+ var changeAmount = available - fee;
908
+ this._removeOutput(this._changeIndex);
909
+ this._changeIndex = undefined;
910
+ if (changeAmount >= Transaction.DUST_AMOUNT) {
911
+ this._changeIndex = this.outputs.length;
912
+ this._addOutput(
913
+ new Output({
914
+ script: this._changeScript,
915
+ satoshis: changeAmount,
916
+ data: this._changeData || ''
917
+ }),
918
+ );
919
+ }
920
+ this._clearSignatures();
921
+ };
922
+ /**
923
+ * Calculates the fee of the transaction.
924
+ *
925
+ * If there's a fixed fee set, return that.
926
+ *
927
+ * If there is no change output set, the fee is the
928
+ * total value of the outputs minus inputs. Note that
929
+ * a serialized transaction only specifies the value
930
+ * of its outputs. (The value of inputs are recorded
931
+ * in the previous transaction outputs being spent.)
932
+ * This method therefore raises a "MissingPreviousOutput"
933
+ * error when called on a serialized transaction.
934
+ *
935
+ * If there's no fee set and no change address,
936
+ * estimate the fee based on size.
937
+ *
938
+ * @return {Number} fee of this transaction in satoshis
939
+ */
940
+ Transaction.prototype.getFee = function () {
941
+ if (this.isCoinbase()) {
942
+ return 0;
943
+ }
944
+ if (!_.isUndefined(this._fee)) {
945
+ return this._fee;
946
+ }
947
+ // if no change output is set, fees should equal all the unspent amount
948
+ if (!this._changeScript) {
949
+ return this.getUnspentValue();
950
+ }
951
+ return this._estimateFee();
952
+ };
953
+
954
+ /**
955
+ * Estimates fee from serialized transaction size in bytes.
956
+ */
957
+ Transaction.prototype._estimateFee = function () {
958
+ var estimatedSize = this._estimateSize();
959
+ return Math.ceil((estimatedSize / 1000) * (this._feePerKb || Transaction.FEE_PER_KB));
960
+ };
961
+
962
+ Transaction.prototype.getUnspentValue = function () {
963
+ return this._getInputAmount() - this._getOutputAmount();
964
+ };
965
+
966
+ Transaction.prototype._clearSignatures = function () {
967
+ _.each(this.inputs, function (input) {
968
+ input.clearSignatures();
969
+ });
970
+ };
971
+
972
+ // 4 version
973
+ // ??? num inputs (VARINT)
974
+ // --- input list ---
975
+ //
976
+ // ??? num outputs (VARINT)
977
+ // --- output list ---
978
+ // 8 value
979
+ // ??? script size (VARINT)
980
+ // ??? script
981
+ //
982
+ // 4 locktime
983
+ Transaction.prototype.getEstimateSize = function () {
984
+ return this._estimateSize();
985
+ };
986
+
987
+ Transaction.prototype._estimateSize = function () {
988
+ var result = 4 + 4; // size of version + size of locktime
989
+ result += Varint(this.inputs.length).toBuffer().length;
990
+ result += Varint(this.outputs.length).toBuffer().length;
991
+ _.each(this.inputs, function (input) {
992
+ result += input._estimateSize();
993
+ });
994
+ _.each(this.outputs, function (output) {
995
+ result += output.getSize();
996
+ });
997
+ return result;
998
+ };
999
+
1000
+ Transaction.prototype._removeOutput = function (index) {
1001
+ var output = this.outputs[index];
1002
+ this.outputs = _.without(this.outputs, output);
1003
+ this._outputAmount = undefined;
1004
+ };
1005
+
1006
+ Transaction.prototype.removeOutput = function (index) {
1007
+ this._removeOutput(index);
1008
+ this._updateChangeOutput();
1009
+ };
1010
+
1011
+ /**
1012
+ * Sort a transaction's inputs and outputs according to BIP69
1013
+ *
1014
+ * @see {https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki}
1015
+ * @return {Transaction} this
1016
+ */
1017
+ Transaction.prototype.sort = function () {
1018
+ this.sortInputs(function (inputs) {
1019
+ var copy = Array.prototype.concat.apply([], inputs);
1020
+ copy.sort(function (first, second) {
1021
+ return first.prevTxId.compare(second.prevTxId) || first.outputIndex - second.outputIndex;
1022
+ });
1023
+ return copy;
1024
+ });
1025
+ this.sortOutputs(function (outputs) {
1026
+ var copy = Array.prototype.concat.apply([], outputs);
1027
+ copy.sort(function (first, second) {
1028
+ return (
1029
+ first.satoshis - second.satoshis ||
1030
+ first.script.toBuffer().compare(second.script.toBuffer())
1031
+ );
1032
+ });
1033
+ return copy;
1034
+ });
1035
+ return this;
1036
+ };
1037
+
1038
+ /**
1039
+ * Randomize this transaction's outputs ordering. The shuffling algorithm is a
1040
+ * version of the Fisher-Yates shuffle.
1041
+ *
1042
+ * @return {Transaction} this
1043
+ */
1044
+ Transaction.prototype.shuffleOutputs = function () {
1045
+ return this.sortOutputs(_.shuffle);
1046
+ };
1047
+
1048
+ /**
1049
+ * Sort this transaction's outputs, according to a given sorting function that
1050
+ * takes an array as argument and returns a new array, with the same elements
1051
+ * but with a different order. The argument function MUST NOT modify the order
1052
+ * of the original array
1053
+ *
1054
+ * @param {Function} sortingFunction
1055
+ * @return {Transaction} this
1056
+ */
1057
+ Transaction.prototype.sortOutputs = function (sortingFunction) {
1058
+ var outs = sortingFunction(this.outputs);
1059
+ return this._newOutputOrder(outs);
1060
+ };
1061
+
1062
+ /**
1063
+ * Sort this transaction's inputs, according to a given sorting function that
1064
+ * takes an array as argument and returns a new array, with the same elements
1065
+ * but with a different order.
1066
+ *
1067
+ * @param {Function} sortingFunction
1068
+ * @return {Transaction} this
1069
+ */
1070
+ Transaction.prototype.sortInputs = function (sortingFunction) {
1071
+ this.inputs = sortingFunction(this.inputs);
1072
+ this._clearSignatures();
1073
+ return this;
1074
+ };
1075
+
1076
+ Transaction.prototype._newOutputOrder = function (newOutputs) {
1077
+ var isInvalidSorting =
1078
+ this.outputs.length !== newOutputs.length ||
1079
+ _.difference(this.outputs, newOutputs).length !== 0;
1080
+ if (isInvalidSorting) {
1081
+ throw new errors.Transaction.InvalidSorting();
1082
+ }
1083
+
1084
+ if (!_.isUndefined(this._changeIndex)) {
1085
+ var changeOutput = this.outputs[this._changeIndex];
1086
+ this._changeIndex = newOutputs.indexOf(changeOutput);
1087
+ }
1088
+
1089
+ this.outputs = newOutputs;
1090
+ return this;
1091
+ };
1092
+
1093
+ Transaction.prototype.removeInput = function (txId, outputIndex) {
1094
+ var index;
1095
+ if (!outputIndex && _.isNumber(txId)) {
1096
+ index = txId;
1097
+ } else {
1098
+ index = _.findIndex(this.inputs, function (input) {
1099
+ return input.prevTxId.toString('hex') === txId && input.outputIndex === outputIndex;
1100
+ });
1101
+ }
1102
+ if (index < 0 || index >= this.inputs.length) {
1103
+ throw new errors.Transaction.InvalidIndex(index, this.inputs.length);
1104
+ }
1105
+ var input = this.inputs[index];
1106
+ this.inputs = _.without(this.inputs, input);
1107
+ this._inputAmount = undefined;
1108
+ this._updateChangeOutput();
1109
+ };
1110
+
1111
+ /* Signature handling */
1112
+
1113
+ /**
1114
+ * Sign the transaction using one or more private keys.
1115
+ *
1116
+ * It tries to sign each input, verifying that the signature will be valid
1117
+ * (matches a public key).
1118
+ *
1119
+ * @param {Array|String|PrivateKey} privateKey
1120
+ * @param {number} sigtype
1121
+ * @return {Transaction} this, for chaining
1122
+ */
1123
+ Transaction.prototype.sign = function (privateKey, sigtype) {
1124
+ $.checkState(
1125
+ this.hasAllUtxoInfo(),
1126
+ 'Not all utxo information is available to sign the transaction.',
1127
+ );
1128
+ var self = this;
1129
+ if (_.isArray(privateKey)) {
1130
+ _.each(privateKey, function (privateKey) {
1131
+ self.sign(privateKey, sigtype);
1132
+ });
1133
+ return this;
1134
+ }
1135
+ _.each(this.getSignatures(privateKey, sigtype), function (signature) {
1136
+ self.applySignature(signature);
1137
+ });
1138
+
1139
+ this._privateKey = privateKey;
1140
+ this._sigType = sigtype;
1141
+ return this;
1142
+ };
1143
+
1144
+ Transaction.prototype.getSignatures = function (privKey, sigtype) {
1145
+ privKey = new PrivateKey(privKey);
1146
+ // By default, signs using SIGHASH_ALL
1147
+ sigtype = sigtype || Signature.SIGHASH_ALL;
1148
+ var transaction = this;
1149
+ var results = [];
1150
+ var hashData = Hash.sha256ripemd160(privKey.publicKey.toBuffer());
1151
+ _.each(this.inputs, function forEachInput(input, index) {
1152
+ _.each(
1153
+ input.getSignatures(transaction, privKey, index, sigtype, hashData),
1154
+ function (signature) {
1155
+ results.push(signature);
1156
+ },
1157
+ );
1158
+ });
1159
+ return results;
1160
+ };
1161
+
1162
+ /**
1163
+ * Add a signature to the transaction
1164
+ *
1165
+ * @param {Object} signature
1166
+ * @param {number} signature.inputIndex
1167
+ * @param {number} signature.sigtype
1168
+ * @param {PublicKey} signature.publicKey
1169
+ * @param {Signature} signature.signature
1170
+ * @return {Transaction} this, for chaining
1171
+ */
1172
+ Transaction.prototype.applySignature = function (signature) {
1173
+ this.inputs[signature.inputIndex].addSignature(this, signature);
1174
+ return this;
1175
+ };
1176
+
1177
+ Transaction.prototype.isFullySigned = function () {
1178
+ _.each(this.inputs, function (input) {
1179
+ if (input.isFullySigned === Input.prototype.isFullySigned) {
1180
+ throw new errors.Transaction.UnableToVerifySignature(
1181
+ 'Unrecognized script kind, or not enough information to execute script.' +
1182
+ 'This usually happens when creating a transaction from a serialized transaction',
1183
+ );
1184
+ }
1185
+ });
1186
+ return _.every(
1187
+ _.map(this.inputs, function (input) {
1188
+ return input.isFullySigned();
1189
+ }),
1190
+ );
1191
+ };
1192
+
1193
+ Transaction.prototype.isValidSignature = function (signature) {
1194
+ var self = this;
1195
+ if (this.inputs[signature.inputIndex].isValidSignature === Input.prototype.isValidSignature) {
1196
+ throw new errors.Transaction.UnableToVerifySignature(
1197
+ 'Unrecognized script kind, or not enough information to execute script.' +
1198
+ 'This usually happens when creating a transaction from a serialized transaction',
1199
+ );
1200
+ }
1201
+ return this.inputs[signature.inputIndex].isValidSignature(self, signature);
1202
+ };
1203
+
1204
+ /**
1205
+ * @returns {bool} whether the signature is valid for this transaction input
1206
+ */
1207
+ Transaction.prototype.verifySignature = function (sig, pubkey, nin) {
1208
+ return Sighash.verify(this, sig, pubkey, nin);
1209
+ };
1210
+
1211
+ /**
1212
+ * Check that a transaction passes basic sanity tests. If not, return a string
1213
+ * describing the error. This function contains the same logic as
1214
+ * CheckTransaction in bitcoin core.
1215
+ */
1216
+ Transaction.prototype.verify = function (notVerifyInput) {
1217
+ // Basic checks that don't depend on any context
1218
+ if (this.inputs.length === 0) {
1219
+ return 'transaction txins empty';
1220
+ }
1221
+
1222
+ if (this.outputs.length === 0) {
1223
+ return 'transaction txouts empty';
1224
+ }
1225
+
1226
+ // Check for negative or overflow output values
1227
+ var valueoutbn = new BN(0);
1228
+ for (var i = 0; i < this.outputs.length; i++) {
1229
+ var txout = this.outputs[i];
1230
+
1231
+ if (txout.invalidSatoshis()) {
1232
+ return 'transaction txout ' + i + ' satoshis is invalid';
1233
+ }
1234
+ if (txout._satoshisBN.gt(new BN(Transaction.MAX_MONEY, 10))) {
1235
+ return 'transaction txout ' + i + ' greater than MAX_MONEY';
1236
+ }
1237
+ valueoutbn = valueoutbn.add(txout._satoshisBN);
1238
+ if (valueoutbn.gt(new BN(Transaction.MAX_MONEY))) {
1239
+ return 'transaction txout ' + i + ' total output greater than MAX_MONEY';
1240
+ }
1241
+ }
1242
+
1243
+ // Check for duplicate inputs
1244
+ var txinmap = {};
1245
+ for (i = 0; i < this.inputs.length; i++) {
1246
+ var txin = this.inputs[i];
1247
+
1248
+ var inputid = txin.prevTxId + ':' + txin.outputIndex;
1249
+ if (!_.isUndefined(txinmap[inputid])) {
1250
+ return 'transaction input ' + i + ' duplicate input';
1251
+ }
1252
+ txinmap[inputid] = true;
1253
+ }
1254
+
1255
+ var isCoinbase = this.isCoinbase();
1256
+ if (isCoinbase) {
1257
+ var buf = this.inputs[0]._scriptBuffer;
1258
+ if (buf.length < 2 || buf.length > 100) {
1259
+ return 'coinbase transaction script size invalid';
1260
+ }
1261
+ } else {
1262
+ for (i = 0; i < this.inputs.length; i++) {
1263
+ if (this.inputs[i].isNull()) {
1264
+ return 'transaction input ' + i + ' has null input';
1265
+ }
1266
+
1267
+ if (!notVerifyInput) {
1268
+ var res = this.inputs[i].verify(this, i);
1269
+ if (!res.success) {
1270
+ return 'transaction input ' + i + ' VerifyError: ' + res.error;
1271
+ }
1272
+ }
1273
+ }
1274
+ }
1275
+ return true;
1276
+ };
1277
+
1278
+ /**
1279
+ * Analogous to bitcoind's IsCoinBase function in transaction.h
1280
+ */
1281
+ Transaction.prototype.isCoinbase = function () {
1282
+ return this.inputs.length === 1 && this.inputs[0].isNull();
1283
+ };
1284
+
1285
+ /**
1286
+ *
1287
+ * @param {number | object} inputIndex or option
1288
+ * @param {Script|(tx, output) => Script} unlockScriptOrCallback unlockScript or a callback returns unlockScript
1289
+ * @returns unlockScript of the special input
1290
+ */
1291
+ Transaction.prototype.setInputScript = function (options, unlockScriptOrCallback) {
1292
+ var inputIndex = 0;
1293
+ var privateKey;
1294
+ var sigtype;
1295
+ var isLowS = false;
1296
+ if (typeof options === 'number') {
1297
+ inputIndex = options;
1298
+ sigtype = Signature.SIGHASH_ALL;
1299
+ } else {
1300
+ inputIndex = options.inputIndex || 0;
1301
+ privateKey = options.privateKey;
1302
+ sigtype = options.sigtype || Signature.SIGHASH_ALL;
1303
+ isLowS = options.isLowS || false;
1304
+ }
1305
+
1306
+ if (unlockScriptOrCallback instanceof Function) {
1307
+ var outputInPrevTx = this.inputs[inputIndex].output;
1308
+ this._inputsMap.set(inputIndex, {
1309
+ sigtype,
1310
+ privateKey,
1311
+ isLowS,
1312
+ callback: unlockScriptOrCallback,
1313
+ });
1314
+ var unlockScript = unlockScriptOrCallback(this, outputInPrevTx);
1315
+ this.inputs[inputIndex].setScript(unlockScript);
1316
+ } else {
1317
+ this.inputs[inputIndex].setScript(unlockScriptOrCallback);
1318
+ }
1319
+
1320
+ return this;
1321
+ };
1322
+
1323
+ /**
1324
+ *
1325
+ * @param {number | object} inputIndex or option
1326
+ * @param {(tx, output) => Promise<Script>} unlockScriptOrCallback a callback returns a unlocking script
1327
+ * @returns A promise which resolves to unlockScript of the special input
1328
+ */
1329
+ Transaction.prototype.setInputScriptAsync = async function (options, unlockScriptOrCallback) {
1330
+ var inputIndex = 0;
1331
+ var sigtype;
1332
+ var isLowS = false;
1333
+ if (typeof options === 'number') {
1334
+ inputIndex = options;
1335
+ sigtype = Signature.SIGHASH_ALL;
1336
+ } else {
1337
+ inputIndex = options.inputIndex || 0;
1338
+ sigtype = options.sigtype || Signature.SIGHASH_ALL;
1339
+ isLowS = options.isLowS || false;
1340
+ }
1341
+
1342
+ if (unlockScriptOrCallback instanceof Function) {
1343
+ var outputInPrevTx = this.inputs[inputIndex].output;
1344
+ this._inputsMap.set(inputIndex, {
1345
+ sigtype,
1346
+ isLowS,
1347
+ callback: unlockScriptOrCallback,
1348
+ });
1349
+ var unlockScript = await unlockScriptOrCallback(this, outputInPrevTx);
1350
+ this.inputs[inputIndex].setScript(unlockScript);
1351
+ } else {
1352
+ throw new errors.InvalidArgument('Must provide a callback returns a unlocking script');
1353
+ }
1354
+
1355
+ return this;
1356
+ };
1357
+
1358
+ Transaction.prototype.setInputSequence = function (inputIndex, sequence) {
1359
+ this.inputs[inputIndex].sequenceNumber = sequence;
1360
+ return this;
1361
+ };
1362
+
1363
+ /**
1364
+ *
1365
+ * @param {number} outputIndex
1366
+ * @param {Output|(tx) => Output} outputOrcb output or a callback returns output
1367
+ * @returns output
1368
+ */
1369
+ Transaction.prototype.setOutput = function (outputIndex, outputOrcb) {
1370
+ if (outputOrcb instanceof Function) {
1371
+ this._outputsMap.set(outputIndex, outputOrcb);
1372
+ this.outputs[outputIndex] = outputOrcb(this);
1373
+ } else {
1374
+ this.outputs[outputIndex] = outputOrcb;
1375
+ }
1376
+
1377
+ this._updateChangeOutput();
1378
+ return this;
1379
+ };
1380
+
1381
+ /**
1382
+ * Seal a transaction. After the transaction is sealed, except for the unlock script entered,
1383
+ * other attributes of the transaction cannot be modified
1384
+ */
1385
+ Transaction.prototype.seal = function () {
1386
+ var self = this;
1387
+
1388
+ this._outputsMap.forEach(function (callback, key) {
1389
+ self.outputs[key] = callback(self);
1390
+ });
1391
+
1392
+ this._updateChangeOutput();
1393
+
1394
+ this._inputsMap.forEach(function (options, key) {
1395
+ var outputInPrevTx = self.inputs[key].output;
1396
+ var unlockScript = options.callback(self, outputInPrevTx);
1397
+ self.inputs[key].setScript(unlockScript);
1398
+ });
1399
+
1400
+ if (this._privateKey) {
1401
+ this.sign(this._privateKey, this._sigType);
1402
+ }
1403
+
1404
+ this.sealed = true;
1405
+
1406
+ return this;
1407
+ };
1408
+
1409
+ /**
1410
+ * Seal a transaction asynchronously. After the transaction is sealed, except for the unlock script entered,
1411
+ * other attributes of the transaction cannot be modified
1412
+ */
1413
+ Transaction.prototype.sealAsync = async function () {
1414
+ var self = this;
1415
+
1416
+ this._outputsMap.forEach(function (callback, key) {
1417
+ self.outputs[key] = callback(self);
1418
+ });
1419
+
1420
+ this._updateChangeOutput();
1421
+
1422
+ var promises = [];
1423
+
1424
+ this._inputsMap.forEach(function (options, key) {
1425
+ var outputInPrevTx = self.inputs[key].output;
1426
+
1427
+ promises.push(
1428
+ Promise.resolve(options.callback(self, outputInPrevTx)).then((unlockScript) => {
1429
+ return { key, unlockScript };
1430
+ }),
1431
+ );
1432
+ });
1433
+
1434
+ await Promise.all(promises).then((items) => {
1435
+ items.forEach(({ key, unlockScript }) => {
1436
+ self.inputs[key].setScript(unlockScript);
1437
+ });
1438
+ });
1439
+
1440
+ if (this._privateKey) {
1441
+ this.sign(this._privateKey, this._sigType);
1442
+ }
1443
+
1444
+ this.sealed = true;
1445
+
1446
+ return this;
1447
+ };
1448
+
1449
+ Transaction.prototype.setLockTime = function (nLockTime) {
1450
+ this.nLockTime = nLockTime;
1451
+ return this;
1452
+ };
1453
+
1454
+ /**
1455
+ *
1456
+ * @returns satoshis of change output
1457
+ */
1458
+ Transaction.prototype.getChangeAmount = function () {
1459
+ if (_.isUndefined(this._changeIndex)) {
1460
+ return 0;
1461
+ }
1462
+
1463
+ return this.outputs[this._changeIndex].satoshis;
1464
+ };
1465
+
1466
+ /**
1467
+ *
1468
+ * @returns estimate fee by transaction size
1469
+ */
1470
+ Transaction.prototype.getEstimateFee = function () {
1471
+ return this._estimateFee();
1472
+ };
1473
+
1474
+ /**
1475
+ *
1476
+ * @param {number} feePerKb the fee per KB for this transaction
1477
+ * @returns true or false
1478
+ */
1479
+ Transaction.prototype.checkFeeRate = function (feePerKb) {
1480
+ var fee = this.getUnspentValue();
1481
+
1482
+ var estimatedSize = this._estimateSize();
1483
+ var expectedRate = (feePerKb || this._feePerKb || Transaction.FEE_PER_KB) / 1000;
1484
+ var realFeeRate = fee / estimatedSize;
1485
+ return realFeeRate >= expectedRate;
1486
+ };
1487
+
1488
+ /**
1489
+ *
1490
+ * @returns the serialization of all input outpoints
1491
+ */
1492
+ Transaction.prototype.prevouts = function () {
1493
+ var writer = new BufferWriter();
1494
+
1495
+ _.each(this.inputs, function (input) {
1496
+ writer.writeReverse(input.prevTxId);
1497
+ writer.writeUInt32LE(input.outputIndex);
1498
+ });
1499
+
1500
+ var buf = writer.toBuffer();
1501
+ return buf.toString('hex');
1502
+ };
1503
+
1504
+ /**
1505
+ *
1506
+ * @returns if the transaction is sealed
1507
+ */
1508
+ Transaction.prototype.isSealed = function () {
1509
+ return this.sealed;
1510
+ };
1511
+
1512
+ Transaction.prototype.getPreimage = function (inputIndex, sigtype, isLowS) {
1513
+ $.checkArgumentType(inputIndex, 'number', 'inputIndex');
1514
+ sigtype = sigtype || Signature.SIGHASH_ALL;
1515
+ isLowS = isLowS || false;
1516
+ inputIndex = inputIndex || 0;
1517
+ return this.inputs[inputIndex].getPreimage(this, inputIndex, sigtype, isLowS);
1518
+ };
1519
+
1520
+ Transaction.prototype.getSignature = function (inputIndex, privateKeys, sigtypes) {
1521
+ $.checkArgumentType(inputIndex, 'number', 'inputIndex');
1522
+ var results = [];
1523
+ var inputOpt = (this._inputsMap || new Map()).get(inputIndex);
1524
+
1525
+ privateKeys = privateKeys || (inputOpt ? inputOpt.privateKey : this._privateKey);
1526
+
1527
+ if (privateKeys) {
1528
+ sigtypes = sigtypes || Signature.SIGHASH_ALL;
1529
+ var sigs = this.inputs[inputIndex].getSignatures(this, privateKeys, inputIndex, sigtypes);
1530
+
1531
+ _.each(sigs, function (sig) {
1532
+ results.push(sig.signature.toTxFormat().toString('hex'));
1533
+ });
1534
+
1535
+ if (results.length === 1) {
1536
+ return results[0];
1537
+ }
1538
+
1539
+ return results;
1540
+ }
1541
+
1542
+ return [];
1543
+ };
1544
+
1545
+ Transaction.prototype.addInputFromPrevTx = function (prevTx, outputIndex) {
1546
+ $.checkArgumentType(prevTx, Transaction, 'prevTx');
1547
+
1548
+ var outputIdx = outputIndex || 0;
1549
+
1550
+ const output = prevTx.outputs[outputIdx];
1551
+
1552
+ if (output.script.isPublicKeyHashOut()) {
1553
+ return this.addInput(
1554
+ new PublicKeyHashInput({
1555
+ prevTxId: prevTx.id,
1556
+ outputIndex: outputIdx,
1557
+ script: new Script(''), // placeholder
1558
+ output: output,
1559
+ }),
1560
+ );
1561
+ } else {
1562
+ return this.addInput(
1563
+ new Input({
1564
+ prevTxId: prevTx.id,
1565
+ outputIndex: outputIdx,
1566
+ script: new Script(''), // placeholder
1567
+ output: output,
1568
+ }),
1569
+ );
1570
+ }
1571
+ };
1572
+
1573
+ Transaction.prototype.addDummyInput = function (script, satoshis) {
1574
+ $.checkArgumentType(script, Script, 'script');
1575
+ $.checkArgumentType(satoshis, 'number', 'satoshis');
1576
+
1577
+ return this.addInput(
1578
+ new Input({
1579
+ prevTxId: 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458',
1580
+ outputIndex: 0,
1581
+ script: new Script(''), // placeholder
1582
+ output: new Output({
1583
+ script: script,
1584
+ satoshis: satoshis,
1585
+ }),
1586
+ }),
1587
+ );
1588
+ };
1589
+
1590
+ /**
1591
+ * Same as change(addresss), but using the address of Transaction.DUMMY_PRIVATEKEY as default change address
1592
+ *
1593
+ * Beware that this resets all the signatures for inputs (in further versions,
1594
+ * SIGHASH_SINGLE or SIGHASH_NONE signatures will not be reset).
1595
+ *
1596
+ * @return {Transaction} this, for chaining
1597
+ */
1598
+ Transaction.prototype.dummyChange = function () {
1599
+ return this.change(Transaction.DUMMY_PRIVATEKEY.toAddress());
1600
+ };
1601
+
1602
+ Transaction.prototype.verifyScript = function (inputIndex) {
1603
+ $.checkArgumentType(inputIndex, 'number', 'inputIndex');
1604
+
1605
+ if (!this.inputs[inputIndex]) {
1606
+ throw new errors.Transaction.Input.MissingInput();
1607
+ }
1608
+
1609
+ return this.inputs[inputIndex].verify(this, inputIndex);
1610
+ };
1611
+
1612
+ /**
1613
+ * @deprecated, please use `verifyScript` instead
1614
+ */
1615
+ Transaction.prototype.verifyInputScript = function (inputIndex) {
1616
+ return this.verifyScript(inputIndex);
1617
+ };
1618
+
1619
+ Transaction.prototype.getInputAmount = function (inputIndex) {
1620
+ $.checkArgumentType(inputIndex, 'number', 'inputIndex');
1621
+
1622
+ if (!this.inputs[inputIndex]) {
1623
+ throw new errors.Transaction.Input.MissingInput();
1624
+ }
1625
+
1626
+ return this.inputs[inputIndex].output.satoshis;
1627
+ };
1628
+
1629
+ Transaction.prototype.getOutputAmount = function (outputIndex) {
1630
+ $.checkArgumentType(outputIndex, 'number', 'outputIndex');
1631
+
1632
+ if (!this.outputs[outputIndex]) {
1633
+ throw new errors.Transaction.MissingOutput();
1634
+ }
1635
+
1636
+ return this.outputs[outputIndex].satoshis;
1637
+ };
1638
+
1639
+ module.exports = Transaction;