1 | 'use strict';
|
2 | Object.defineProperty(exports, '__esModule', { value: true });
|
3 | const bufferutils = require('./bufferutils');
|
4 | const bufferutils_1 = require('./bufferutils');
|
5 | const bcrypto = require('./crypto');
|
6 | const bscript = require('./script');
|
7 | const script_1 = require('./script');
|
8 | const types = require('./types');
|
9 | const typeforce = require('typeforce');
|
10 | const varuint = require('varuint-bitcoin');
|
11 | function varSliceSize(someScript) {
|
12 | const length = someScript.length;
|
13 | return varuint.encodingLength(length) + length;
|
14 | }
|
15 | function vectorSize(someVector) {
|
16 | const length = someVector.length;
|
17 | return (
|
18 | varuint.encodingLength(length) +
|
19 | someVector.reduce((sum, witness) => {
|
20 | return sum + varSliceSize(witness);
|
21 | }, 0)
|
22 | );
|
23 | }
|
24 | const EMPTY_SCRIPT = Buffer.allocUnsafe(0);
|
25 | const EMPTY_WITNESS = [];
|
26 | const ZERO = Buffer.from(
|
27 | '0000000000000000000000000000000000000000000000000000000000000000',
|
28 | 'hex',
|
29 | );
|
30 | const ONE = Buffer.from(
|
31 | '0000000000000000000000000000000000000000000000000000000000000001',
|
32 | 'hex',
|
33 | );
|
34 | const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex');
|
35 | const BLANK_OUTPUT = {
|
36 | script: EMPTY_SCRIPT,
|
37 | valueBuffer: VALUE_UINT64_MAX,
|
38 | };
|
39 | function isOutput(out) {
|
40 | return out.value !== undefined;
|
41 | }
|
42 | class Transaction {
|
43 | constructor() {
|
44 | this.version = 1;
|
45 | this.locktime = 0;
|
46 | this.ins = [];
|
47 | this.outs = [];
|
48 | }
|
49 | static fromBuffer(buffer, _NO_STRICT) {
|
50 | let offset = 0;
|
51 | function readSlice(n) {
|
52 | offset += n;
|
53 | return buffer.slice(offset - n, offset);
|
54 | }
|
55 | function readUInt32() {
|
56 | const i = buffer.readUInt32LE(offset);
|
57 | offset += 4;
|
58 | return i;
|
59 | }
|
60 | function readInt32() {
|
61 | const i = buffer.readInt32LE(offset);
|
62 | offset += 4;
|
63 | return i;
|
64 | }
|
65 | function readUInt64() {
|
66 | const i = bufferutils.readUInt64LE(buffer, offset);
|
67 | offset += 8;
|
68 | return i;
|
69 | }
|
70 | function readVarInt() {
|
71 | const vi = varuint.decode(buffer, offset);
|
72 | offset += varuint.decode.bytes;
|
73 | return vi;
|
74 | }
|
75 | function readVarSlice() {
|
76 | return readSlice(readVarInt());
|
77 | }
|
78 | function readVector() {
|
79 | const count = readVarInt();
|
80 | const vector = [];
|
81 | for (let i = 0; i < count; i++) vector.push(readVarSlice());
|
82 | return vector;
|
83 | }
|
84 | const tx = new Transaction();
|
85 | tx.version = readInt32();
|
86 | const marker = buffer.readUInt8(offset);
|
87 | const flag = buffer.readUInt8(offset + 1);
|
88 | let hasWitnesses = false;
|
89 | if (
|
90 | marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
|
91 | flag === Transaction.ADVANCED_TRANSACTION_FLAG
|
92 | ) {
|
93 | offset += 2;
|
94 | hasWitnesses = true;
|
95 | }
|
96 | const vinLen = readVarInt();
|
97 | for (let i = 0; i < vinLen; ++i) {
|
98 | tx.ins.push({
|
99 | hash: readSlice(32),
|
100 | index: readUInt32(),
|
101 | script: readVarSlice(),
|
102 | sequence: readUInt32(),
|
103 | witness: EMPTY_WITNESS,
|
104 | });
|
105 | }
|
106 | const voutLen = readVarInt();
|
107 | for (let i = 0; i < voutLen; ++i) {
|
108 | tx.outs.push({
|
109 | value: readUInt64(),
|
110 | script: readVarSlice(),
|
111 | });
|
112 | }
|
113 | if (hasWitnesses) {
|
114 | for (let i = 0; i < vinLen; ++i) {
|
115 | tx.ins[i].witness = readVector();
|
116 | }
|
117 |
|
118 | if (!tx.hasWitnesses())
|
119 | throw new Error('Transaction has superfluous witness data');
|
120 | }
|
121 | tx.locktime = readUInt32();
|
122 | if (_NO_STRICT) return tx;
|
123 | if (offset !== buffer.length)
|
124 | throw new Error('Transaction has unexpected data');
|
125 | return tx;
|
126 | }
|
127 | static fromHex(hex) {
|
128 | return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false);
|
129 | }
|
130 | static isCoinbaseHash(buffer) {
|
131 | typeforce(types.Hash256bit, buffer);
|
132 | for (let i = 0; i < 32; ++i) {
|
133 | if (buffer[i] !== 0) return false;
|
134 | }
|
135 | return true;
|
136 | }
|
137 | isCoinbase() {
|
138 | return (
|
139 | this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)
|
140 | );
|
141 | }
|
142 | addInput(hash, index, sequence, scriptSig) {
|
143 | typeforce(
|
144 | types.tuple(
|
145 | types.Hash256bit,
|
146 | types.UInt32,
|
147 | types.maybe(types.UInt32),
|
148 | types.maybe(types.Buffer),
|
149 | ),
|
150 | arguments,
|
151 | );
|
152 | if (types.Null(sequence)) {
|
153 | sequence = Transaction.DEFAULT_SEQUENCE;
|
154 | }
|
155 |
|
156 | return (
|
157 | this.ins.push({
|
158 | hash,
|
159 | index,
|
160 | script: scriptSig || EMPTY_SCRIPT,
|
161 | sequence: sequence,
|
162 | witness: EMPTY_WITNESS,
|
163 | }) - 1
|
164 | );
|
165 | }
|
166 | addOutput(scriptPubKey, value) {
|
167 | typeforce(types.tuple(types.Buffer, types.Satoshi), arguments);
|
168 |
|
169 | return (
|
170 | this.outs.push({
|
171 | script: scriptPubKey,
|
172 | value,
|
173 | }) - 1
|
174 | );
|
175 | }
|
176 | hasWitnesses() {
|
177 | return this.ins.some(x => {
|
178 | return x.witness.length !== 0;
|
179 | });
|
180 | }
|
181 | weight() {
|
182 | const base = this.__byteLength(false);
|
183 | const total = this.__byteLength(true);
|
184 | return base * 3 + total;
|
185 | }
|
186 | virtualSize() {
|
187 | return Math.ceil(this.weight() / 4);
|
188 | }
|
189 | byteLength() {
|
190 | return this.__byteLength(true);
|
191 | }
|
192 | clone() {
|
193 | const newTx = new Transaction();
|
194 | newTx.version = this.version;
|
195 | newTx.locktime = this.locktime;
|
196 | newTx.ins = this.ins.map(txIn => {
|
197 | return {
|
198 | hash: txIn.hash,
|
199 | index: txIn.index,
|
200 | script: txIn.script,
|
201 | sequence: txIn.sequence,
|
202 | witness: txIn.witness,
|
203 | };
|
204 | });
|
205 | newTx.outs = this.outs.map(txOut => {
|
206 | return {
|
207 | script: txOut.script,
|
208 | value: txOut.value,
|
209 | };
|
210 | });
|
211 | return newTx;
|
212 | }
|
213 | |
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 | hashForSignature(inIndex, prevOutScript, hashType) {
|
222 | typeforce(
|
223 | types.tuple(types.UInt32, types.Buffer, types.Number),
|
224 | arguments,
|
225 | );
|
226 |
|
227 | if (inIndex >= this.ins.length) return ONE;
|
228 |
|
229 | const ourScript = bscript.compile(
|
230 | bscript.decompile(prevOutScript).filter(x => {
|
231 | return x !== script_1.OPS.OP_CODESEPARATOR;
|
232 | }),
|
233 | );
|
234 | const txTmp = this.clone();
|
235 |
|
236 | if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
|
237 | txTmp.outs = [];
|
238 |
|
239 | txTmp.ins.forEach((input, i) => {
|
240 | if (i === inIndex) return;
|
241 | input.sequence = 0;
|
242 | });
|
243 |
|
244 | } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
|
245 |
|
246 | if (inIndex >= this.outs.length) return ONE;
|
247 |
|
248 | txTmp.outs.length = inIndex + 1;
|
249 |
|
250 | for (let i = 0; i < inIndex; i++) {
|
251 | txTmp.outs[i] = BLANK_OUTPUT;
|
252 | }
|
253 |
|
254 | txTmp.ins.forEach((input, y) => {
|
255 | if (y === inIndex) return;
|
256 | input.sequence = 0;
|
257 | });
|
258 | }
|
259 |
|
260 | if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
|
261 | txTmp.ins = [txTmp.ins[inIndex]];
|
262 | txTmp.ins[0].script = ourScript;
|
263 |
|
264 | } else {
|
265 |
|
266 | txTmp.ins.forEach(input => {
|
267 | input.script = EMPTY_SCRIPT;
|
268 | });
|
269 | txTmp.ins[inIndex].script = ourScript;
|
270 | }
|
271 |
|
272 | const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4);
|
273 | buffer.writeInt32LE(hashType, buffer.length - 4);
|
274 | txTmp.__toBuffer(buffer, 0, false);
|
275 | return bcrypto.hash256(buffer);
|
276 | }
|
277 | hashForWitnessV0(inIndex, prevOutScript, value, hashType) {
|
278 | typeforce(
|
279 | types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32),
|
280 | arguments,
|
281 | );
|
282 | let tbuffer = Buffer.from([]);
|
283 | let toffset = 0;
|
284 | function writeSlice(slice) {
|
285 | toffset += slice.copy(tbuffer, toffset);
|
286 | }
|
287 | function writeUInt32(i) {
|
288 | toffset = tbuffer.writeUInt32LE(i, toffset);
|
289 | }
|
290 | function writeUInt64(i) {
|
291 | toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset);
|
292 | }
|
293 | function writeVarInt(i) {
|
294 | varuint.encode(i, tbuffer, toffset);
|
295 | toffset += varuint.encode.bytes;
|
296 | }
|
297 | function writeVarSlice(slice) {
|
298 | writeVarInt(slice.length);
|
299 | writeSlice(slice);
|
300 | }
|
301 | let hashOutputs = ZERO;
|
302 | let hashPrevouts = ZERO;
|
303 | let hashSequence = ZERO;
|
304 | if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
|
305 | tbuffer = Buffer.allocUnsafe(36 * this.ins.length);
|
306 | toffset = 0;
|
307 | this.ins.forEach(txIn => {
|
308 | writeSlice(txIn.hash);
|
309 | writeUInt32(txIn.index);
|
310 | });
|
311 | hashPrevouts = bcrypto.hash256(tbuffer);
|
312 | }
|
313 | if (
|
314 | !(hashType & Transaction.SIGHASH_ANYONECANPAY) &&
|
315 | (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
|
316 | (hashType & 0x1f) !== Transaction.SIGHASH_NONE
|
317 | ) {
|
318 | tbuffer = Buffer.allocUnsafe(4 * this.ins.length);
|
319 | toffset = 0;
|
320 | this.ins.forEach(txIn => {
|
321 | writeUInt32(txIn.sequence);
|
322 | });
|
323 | hashSequence = bcrypto.hash256(tbuffer);
|
324 | }
|
325 | if (
|
326 | (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
|
327 | (hashType & 0x1f) !== Transaction.SIGHASH_NONE
|
328 | ) {
|
329 | const txOutsSize = this.outs.reduce((sum, output) => {
|
330 | return sum + 8 + varSliceSize(output.script);
|
331 | }, 0);
|
332 | tbuffer = Buffer.allocUnsafe(txOutsSize);
|
333 | toffset = 0;
|
334 | this.outs.forEach(out => {
|
335 | writeUInt64(out.value);
|
336 | writeVarSlice(out.script);
|
337 | });
|
338 | hashOutputs = bcrypto.hash256(tbuffer);
|
339 | } else if (
|
340 | (hashType & 0x1f) === Transaction.SIGHASH_SINGLE &&
|
341 | inIndex < this.outs.length
|
342 | ) {
|
343 | const output = this.outs[inIndex];
|
344 | tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script));
|
345 | toffset = 0;
|
346 | writeUInt64(output.value);
|
347 | writeVarSlice(output.script);
|
348 | hashOutputs = bcrypto.hash256(tbuffer);
|
349 | }
|
350 | tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript));
|
351 | toffset = 0;
|
352 | const input = this.ins[inIndex];
|
353 | writeUInt32(this.version);
|
354 | writeSlice(hashPrevouts);
|
355 | writeSlice(hashSequence);
|
356 | writeSlice(input.hash);
|
357 | writeUInt32(input.index);
|
358 | writeVarSlice(prevOutScript);
|
359 | writeUInt64(value);
|
360 | writeUInt32(input.sequence);
|
361 | writeSlice(hashOutputs);
|
362 | writeUInt32(this.locktime);
|
363 | writeUInt32(hashType);
|
364 | return bcrypto.hash256(tbuffer);
|
365 | }
|
366 | getHash(forWitness) {
|
367 |
|
368 | if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0);
|
369 | return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness));
|
370 | }
|
371 | getId() {
|
372 |
|
373 | return bufferutils_1.reverseBuffer(this.getHash(false)).toString('hex');
|
374 | }
|
375 | toBuffer(buffer, initialOffset) {
|
376 | return this.__toBuffer(buffer, initialOffset, true);
|
377 | }
|
378 | toHex() {
|
379 | return this.toBuffer(undefined, undefined).toString('hex');
|
380 | }
|
381 | setInputScript(index, scriptSig) {
|
382 | typeforce(types.tuple(types.Number, types.Buffer), arguments);
|
383 | this.ins[index].script = scriptSig;
|
384 | }
|
385 | setWitness(index, witness) {
|
386 | typeforce(types.tuple(types.Number, [types.Buffer]), arguments);
|
387 | this.ins[index].witness = witness;
|
388 | }
|
389 | __byteLength(_ALLOW_WITNESS) {
|
390 | const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
|
391 | return (
|
392 | (hasWitnesses ? 10 : 8) +
|
393 | varuint.encodingLength(this.ins.length) +
|
394 | varuint.encodingLength(this.outs.length) +
|
395 | this.ins.reduce((sum, input) => {
|
396 | return sum + 40 + varSliceSize(input.script);
|
397 | }, 0) +
|
398 | this.outs.reduce((sum, output) => {
|
399 | return sum + 8 + varSliceSize(output.script);
|
400 | }, 0) +
|
401 | (hasWitnesses
|
402 | ? this.ins.reduce((sum, input) => {
|
403 | return sum + vectorSize(input.witness);
|
404 | }, 0)
|
405 | : 0)
|
406 | );
|
407 | }
|
408 | __toBuffer(buffer, initialOffset, _ALLOW_WITNESS) {
|
409 | if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS));
|
410 | let offset = initialOffset || 0;
|
411 | function writeSlice(slice) {
|
412 | offset += slice.copy(buffer, offset);
|
413 | }
|
414 | function writeUInt8(i) {
|
415 | offset = buffer.writeUInt8(i, offset);
|
416 | }
|
417 | function writeUInt32(i) {
|
418 | offset = buffer.writeUInt32LE(i, offset);
|
419 | }
|
420 | function writeInt32(i) {
|
421 | offset = buffer.writeInt32LE(i, offset);
|
422 | }
|
423 | function writeUInt64(i) {
|
424 | offset = bufferutils.writeUInt64LE(buffer, i, offset);
|
425 | }
|
426 | function writeVarInt(i) {
|
427 | varuint.encode(i, buffer, offset);
|
428 | offset += varuint.encode.bytes;
|
429 | }
|
430 | function writeVarSlice(slice) {
|
431 | writeVarInt(slice.length);
|
432 | writeSlice(slice);
|
433 | }
|
434 | function writeVector(vector) {
|
435 | writeVarInt(vector.length);
|
436 | vector.forEach(writeVarSlice);
|
437 | }
|
438 | writeInt32(this.version);
|
439 | const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
|
440 | if (hasWitnesses) {
|
441 | writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER);
|
442 | writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG);
|
443 | }
|
444 | writeVarInt(this.ins.length);
|
445 | this.ins.forEach(txIn => {
|
446 | writeSlice(txIn.hash);
|
447 | writeUInt32(txIn.index);
|
448 | writeVarSlice(txIn.script);
|
449 | writeUInt32(txIn.sequence);
|
450 | });
|
451 | writeVarInt(this.outs.length);
|
452 | this.outs.forEach(txOut => {
|
453 | if (isOutput(txOut)) {
|
454 | writeUInt64(txOut.value);
|
455 | } else {
|
456 | writeSlice(txOut.valueBuffer);
|
457 | }
|
458 | writeVarSlice(txOut.script);
|
459 | });
|
460 | if (hasWitnesses) {
|
461 | this.ins.forEach(input => {
|
462 | writeVector(input.witness);
|
463 | });
|
464 | }
|
465 | writeUInt32(this.locktime);
|
466 |
|
467 | if (initialOffset !== undefined) return buffer.slice(initialOffset, offset);
|
468 | return buffer;
|
469 | }
|
470 | }
|
471 | Transaction.DEFAULT_SEQUENCE = 0xffffffff;
|
472 | Transaction.SIGHASH_ALL = 0x01;
|
473 | Transaction.SIGHASH_NONE = 0x02;
|
474 | Transaction.SIGHASH_SINGLE = 0x03;
|
475 | Transaction.SIGHASH_ANYONECANPAY = 0x80;
|
476 | Transaction.ADVANCED_TRANSACTION_MARKER = 0x00;
|
477 | Transaction.ADVANCED_TRANSACTION_FLAG = 0x01;
|
478 | exports.Transaction = Transaction;
|