@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.
- package/.mocharc.yaml +3 -0
- package/index.d.ts +1541 -0
- package/index.js +74 -0
- package/lib/address.js +478 -0
- package/lib/block/block.js +277 -0
- package/lib/block/blockheader.js +295 -0
- package/lib/block/index.js +4 -0
- package/lib/block/merkleblock.js +323 -0
- package/lib/bn.js +3423 -0
- package/lib/crypto/bn.js +278 -0
- package/lib/crypto/ecdsa.js +339 -0
- package/lib/crypto/hash.browser.js +171 -0
- package/lib/crypto/hash.js +2 -0
- package/lib/crypto/hash.node.js +171 -0
- package/lib/crypto/point.js +221 -0
- package/lib/crypto/random.browser.js +28 -0
- package/lib/crypto/random.js +2 -0
- package/lib/crypto/random.node.js +11 -0
- package/lib/crypto/signature.js +325 -0
- package/lib/encoding/base58.js +111 -0
- package/lib/encoding/base58check.js +121 -0
- package/lib/encoding/bufferreader.js +212 -0
- package/lib/encoding/bufferwriter.js +140 -0
- package/lib/encoding/decode-asm.js +24 -0
- package/lib/encoding/decode-hex.js +32 -0
- package/lib/encoding/decode-script-chunks.js +43 -0
- package/lib/encoding/encode-hex.js +284 -0
- package/lib/encoding/is-hex.js +7 -0
- package/lib/encoding/varint.js +75 -0
- package/lib/errors/index.js +54 -0
- package/lib/errors/spec.js +314 -0
- package/lib/hash-cache.js +50 -0
- package/lib/hdprivatekey.js +678 -0
- package/lib/hdpublickey.js +525 -0
- package/lib/message/message.js +191 -0
- package/lib/mnemonic/mnemonic.js +303 -0
- package/lib/mnemonic/pbkdf2.browser.js +68 -0
- package/lib/mnemonic/pbkdf2.js +2 -0
- package/lib/mnemonic/pbkdf2.node.js +68 -0
- package/lib/mnemonic/words/chinese.js +2054 -0
- package/lib/mnemonic/words/english.js +2054 -0
- package/lib/mnemonic/words/french.js +2054 -0
- package/lib/mnemonic/words/index.js +8 -0
- package/lib/mnemonic/words/italian.js +2054 -0
- package/lib/mnemonic/words/japanese.js +2054 -0
- package/lib/mnemonic/words/spanish.js +2054 -0
- package/lib/networks.js +379 -0
- package/lib/opcode.js +255 -0
- package/lib/privatekey.js +374 -0
- package/lib/publickey.js +386 -0
- package/lib/script/index.js +5 -0
- package/lib/script/interpreter.js +1834 -0
- package/lib/script/script.js +1074 -0
- package/lib/script/stack.js +109 -0
- package/lib/script/write-i32-le.js +17 -0
- package/lib/script/write-push-data.js +35 -0
- package/lib/script/write-u16-le.js +12 -0
- package/lib/script/write-u32-le.js +16 -0
- package/lib/script/write-u64-le.js +24 -0
- package/lib/script/write-u8-le.js +8 -0
- package/lib/script/write-varint.js +46 -0
- package/lib/transaction/index.js +7 -0
- package/lib/transaction/input/index.js +5 -0
- package/lib/transaction/input/input.js +354 -0
- package/lib/transaction/input/multisig.js +242 -0
- package/lib/transaction/input/publickey.js +100 -0
- package/lib/transaction/input/publickeyhash.js +118 -0
- package/lib/transaction/output.js +231 -0
- package/lib/transaction/sighash.js +167 -0
- package/lib/transaction/signature.js +97 -0
- package/lib/transaction/transaction.js +1639 -0
- package/lib/transaction/unspentoutput.js +113 -0
- package/lib/util/_.js +47 -0
- package/lib/util/js.js +90 -0
- package/lib/util/preconditions.js +33 -0
- package/package.json +26 -0
- package/test/address.js +509 -0
- package/test/block/block.js +251 -0
- package/test/block/blockheader.js +275 -0
- package/test/block/merklebloack.js +211 -0
- package/test/crypto/bn.js +177 -0
- package/test/crypto/ecdsa.js +391 -0
- package/test/crypto/hash.browser.js +135 -0
- package/test/crypto/hash.js +136 -0
- package/test/crypto/point.js +224 -0
- package/test/crypto/random.js +32 -0
- package/test/crypto/signature.js +409 -0
- package/test/data/bip69.json +215 -0
- package/test/data/bitcoind/base58_keys_invalid.json +52 -0
- package/test/data/bitcoind/base58_keys_valid.json +335 -0
- package/test/data/bitcoind/blocks.json +22 -0
- package/test/data/bitcoind/script_tests.json +3822 -0
- package/test/data/bitcoind/sig_canonical.json +7 -0
- package/test/data/bitcoind/sig_noncanonical.json +36 -0
- package/test/data/bitcoind/tx_invalid.json +445 -0
- package/test/data/bitcoind/tx_valid.json +44 -0
- package/test/data/blk86756-testnet.dat +0 -0
- package/test/data/blk86756-testnet.js +14 -0
- package/test/data/blk86756-testnet.json +684 -0
- package/test/data/block.hex +1 -0
- package/test/data/ecdsa.json +230 -0
- package/test/data/merkleblocks.js +488 -0
- package/test/data/messages.json +22 -0
- package/test/data/sighash.json +12 -0
- package/test/data/tx_creation.json +95 -0
- package/test/encoding/base58.js +131 -0
- package/test/encoding/base58check.js +136 -0
- package/test/encoding/bufferreader.js +337 -0
- package/test/encoding/bufferwriter.js +172 -0
- package/test/encoding/varint.js +104 -0
- package/test/hashCache.js +67 -0
- package/test/hdkeys.js +445 -0
- package/test/hdprivatekey.js +332 -0
- package/test/hdpublickey.js +304 -0
- package/test/index.js +16 -0
- package/test/message/message.js +204 -0
- package/test/mnemonic/data/fixtures.json +300 -0
- package/test/mnemonic/mnemonic.js +259 -0
- package/test/mnemonic/mocha.opts +1 -0
- package/test/mnemonic/pbkdf2.test.js +59 -0
- package/test/networks.js +159 -0
- package/test/opcode.js +161 -0
- package/test/privatekey.js +439 -0
- package/test/publickey.js +554 -0
- package/test/script/interpreter.js +734 -0
- package/test/script/script.js +1437 -0
- package/test/transaction/deserialize.js +34 -0
- package/test/transaction/input/input.js +90 -0
- package/test/transaction/input/multisig.js +90 -0
- package/test/transaction/input/publickey.js +68 -0
- package/test/transaction/input/publickeyhash.js +51 -0
- package/test/transaction/output.js +185 -0
- package/test/transaction/sighash.js +65 -0
- package/test/transaction/signature.js +114 -0
- package/test/transaction/transaction.js +1109 -0
- package/test/transaction/unspentoutput.js +110 -0
- package/test/util/js.js +76 -0
- 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;
|