UNPKG

18.6 kBJavaScriptView Raw
1'use strict';
2Object.defineProperty(exports, '__esModule', { value: true });
3exports.Transaction = void 0;
4const bufferutils_1 = require('./bufferutils');
5const bcrypto = require('./crypto');
6const bscript = require('./script');
7const script_1 = require('./script');
8const types = require('./types');
9const { typeforce } = types;
10function varSliceSize(someScript) {
11 const length = someScript.length;
12 return bufferutils_1.varuint.encodingLength(length) + length;
13}
14function vectorSize(someVector) {
15 const length = someVector.length;
16 return (
17 bufferutils_1.varuint.encodingLength(length) +
18 someVector.reduce((sum, witness) => {
19 return sum + varSliceSize(witness);
20 }, 0)
21 );
22}
23const EMPTY_BUFFER = Buffer.allocUnsafe(0);
24const EMPTY_WITNESS = [];
25const ZERO = Buffer.from(
26 '0000000000000000000000000000000000000000000000000000000000000000',
27 'hex',
28);
29const ONE = Buffer.from(
30 '0000000000000000000000000000000000000000000000000000000000000001',
31 'hex',
32);
33const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex');
34const BLANK_OUTPUT = {
35 script: EMPTY_BUFFER,
36 valueBuffer: VALUE_UINT64_MAX,
37};
38function isOutput(out) {
39 return out.value !== undefined;
40}
41class Transaction {
42 constructor() {
43 this.version = 1;
44 this.locktime = 0;
45 this.ins = [];
46 this.outs = [];
47 }
48 static fromBuffer(buffer, _NO_STRICT) {
49 const bufferReader = new bufferutils_1.BufferReader(buffer);
50 const tx = new Transaction();
51 tx.version = bufferReader.readInt32();
52 const marker = bufferReader.readUInt8();
53 const flag = bufferReader.readUInt8();
54 let hasWitnesses = false;
55 if (
56 marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
57 flag === Transaction.ADVANCED_TRANSACTION_FLAG
58 ) {
59 hasWitnesses = true;
60 } else {
61 bufferReader.offset -= 2;
62 }
63 const vinLen = bufferReader.readVarInt();
64 for (let i = 0; i < vinLen; ++i) {
65 tx.ins.push({
66 hash: bufferReader.readSlice(32),
67 index: bufferReader.readUInt32(),
68 script: bufferReader.readVarSlice(),
69 sequence: bufferReader.readUInt32(),
70 witness: EMPTY_WITNESS,
71 });
72 }
73 const voutLen = bufferReader.readVarInt();
74 for (let i = 0; i < voutLen; ++i) {
75 tx.outs.push({
76 value: bufferReader.readUInt64(),
77 script: bufferReader.readVarSlice(),
78 });
79 }
80 if (hasWitnesses) {
81 for (let i = 0; i < vinLen; ++i) {
82 tx.ins[i].witness = bufferReader.readVector();
83 }
84 // was this pointless?
85 if (!tx.hasWitnesses())
86 throw new Error('Transaction has superfluous witness data');
87 }
88 tx.locktime = bufferReader.readUInt32();
89 if (_NO_STRICT) return tx;
90 if (bufferReader.offset !== buffer.length)
91 throw new Error('Transaction has unexpected data');
92 return tx;
93 }
94 static fromHex(hex) {
95 return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false);
96 }
97 static isCoinbaseHash(buffer) {
98 typeforce(types.Hash256bit, buffer);
99 for (let i = 0; i < 32; ++i) {
100 if (buffer[i] !== 0) return false;
101 }
102 return true;
103 }
104 isCoinbase() {
105 return (
106 this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)
107 );
108 }
109 addInput(hash, index, sequence, scriptSig) {
110 typeforce(
111 types.tuple(
112 types.Hash256bit,
113 types.UInt32,
114 types.maybe(types.UInt32),
115 types.maybe(types.Buffer),
116 ),
117 arguments,
118 );
119 if (types.Null(sequence)) {
120 sequence = Transaction.DEFAULT_SEQUENCE;
121 }
122 // Add the input and return the input's index
123 return (
124 this.ins.push({
125 hash,
126 index,
127 script: scriptSig || EMPTY_BUFFER,
128 sequence: sequence,
129 witness: EMPTY_WITNESS,
130 }) - 1
131 );
132 }
133 addOutput(scriptPubKey, value) {
134 typeforce(types.tuple(types.Buffer, types.Satoshi), arguments);
135 // Add the output and return the output's index
136 return (
137 this.outs.push({
138 script: scriptPubKey,
139 value,
140 }) - 1
141 );
142 }
143 hasWitnesses() {
144 return this.ins.some(x => {
145 return x.witness.length !== 0;
146 });
147 }
148 weight() {
149 const base = this.byteLength(false);
150 const total = this.byteLength(true);
151 return base * 3 + total;
152 }
153 virtualSize() {
154 return Math.ceil(this.weight() / 4);
155 }
156 byteLength(_ALLOW_WITNESS = true) {
157 const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
158 return (
159 (hasWitnesses ? 10 : 8) +
160 bufferutils_1.varuint.encodingLength(this.ins.length) +
161 bufferutils_1.varuint.encodingLength(this.outs.length) +
162 this.ins.reduce((sum, input) => {
163 return sum + 40 + varSliceSize(input.script);
164 }, 0) +
165 this.outs.reduce((sum, output) => {
166 return sum + 8 + varSliceSize(output.script);
167 }, 0) +
168 (hasWitnesses
169 ? this.ins.reduce((sum, input) => {
170 return sum + vectorSize(input.witness);
171 }, 0)
172 : 0)
173 );
174 }
175 clone() {
176 const newTx = new Transaction();
177 newTx.version = this.version;
178 newTx.locktime = this.locktime;
179 newTx.ins = this.ins.map(txIn => {
180 return {
181 hash: txIn.hash,
182 index: txIn.index,
183 script: txIn.script,
184 sequence: txIn.sequence,
185 witness: txIn.witness,
186 };
187 });
188 newTx.outs = this.outs.map(txOut => {
189 return {
190 script: txOut.script,
191 value: txOut.value,
192 };
193 });
194 return newTx;
195 }
196 /**
197 * Hash transaction for signing a specific input.
198 *
199 * Bitcoin uses a different hash for each signed transaction input.
200 * This method copies the transaction, makes the necessary changes based on the
201 * hashType, and then hashes the result.
202 * This hash can then be used to sign the provided transaction input.
203 */
204 hashForSignature(inIndex, prevOutScript, hashType) {
205 typeforce(
206 types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number),
207 arguments,
208 );
209 // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
210 if (inIndex >= this.ins.length) return ONE;
211 // ignore OP_CODESEPARATOR
212 const ourScript = bscript.compile(
213 bscript.decompile(prevOutScript).filter(x => {
214 return x !== script_1.OPS.OP_CODESEPARATOR;
215 }),
216 );
217 const txTmp = this.clone();
218 // SIGHASH_NONE: ignore all outputs? (wildcard payee)
219 if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
220 txTmp.outs = [];
221 // ignore sequence numbers (except at inIndex)
222 txTmp.ins.forEach((input, i) => {
223 if (i === inIndex) return;
224 input.sequence = 0;
225 });
226 // SIGHASH_SINGLE: ignore all outputs, except at the same index?
227 } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
228 // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
229 if (inIndex >= this.outs.length) return ONE;
230 // truncate outputs after
231 txTmp.outs.length = inIndex + 1;
232 // "blank" outputs before
233 for (let i = 0; i < inIndex; i++) {
234 txTmp.outs[i] = BLANK_OUTPUT;
235 }
236 // ignore sequence numbers (except at inIndex)
237 txTmp.ins.forEach((input, y) => {
238 if (y === inIndex) return;
239 input.sequence = 0;
240 });
241 }
242 // SIGHASH_ANYONECANPAY: ignore inputs entirely?
243 if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
244 txTmp.ins = [txTmp.ins[inIndex]];
245 txTmp.ins[0].script = ourScript;
246 // SIGHASH_ALL: only ignore input scripts
247 } else {
248 // "blank" others input scripts
249 txTmp.ins.forEach(input => {
250 input.script = EMPTY_BUFFER;
251 });
252 txTmp.ins[inIndex].script = ourScript;
253 }
254 // serialize and hash
255 const buffer = Buffer.allocUnsafe(txTmp.byteLength(false) + 4);
256 buffer.writeInt32LE(hashType, buffer.length - 4);
257 txTmp.__toBuffer(buffer, 0, false);
258 return bcrypto.hash256(buffer);
259 }
260 hashForWitnessV1(inIndex, prevOutScripts, values, hashType, leafHash, annex) {
261 // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#common-signature-message
262 typeforce(
263 types.tuple(
264 types.UInt32,
265 typeforce.arrayOf(types.Buffer),
266 typeforce.arrayOf(types.Satoshi),
267 types.UInt32,
268 ),
269 arguments,
270 );
271 if (
272 values.length !== this.ins.length ||
273 prevOutScripts.length !== this.ins.length
274 ) {
275 throw new Error('Must supply prevout script and value for all inputs');
276 }
277 const outputType =
278 hashType === Transaction.SIGHASH_DEFAULT
279 ? Transaction.SIGHASH_ALL
280 : hashType & Transaction.SIGHASH_OUTPUT_MASK;
281 const inputType = hashType & Transaction.SIGHASH_INPUT_MASK;
282 const isAnyoneCanPay = inputType === Transaction.SIGHASH_ANYONECANPAY;
283 const isNone = outputType === Transaction.SIGHASH_NONE;
284 const isSingle = outputType === Transaction.SIGHASH_SINGLE;
285 let hashPrevouts = EMPTY_BUFFER;
286 let hashAmounts = EMPTY_BUFFER;
287 let hashScriptPubKeys = EMPTY_BUFFER;
288 let hashSequences = EMPTY_BUFFER;
289 let hashOutputs = EMPTY_BUFFER;
290 if (!isAnyoneCanPay) {
291 let bufferWriter = bufferutils_1.BufferWriter.withCapacity(
292 36 * this.ins.length,
293 );
294 this.ins.forEach(txIn => {
295 bufferWriter.writeSlice(txIn.hash);
296 bufferWriter.writeUInt32(txIn.index);
297 });
298 hashPrevouts = bcrypto.sha256(bufferWriter.end());
299 bufferWriter = bufferutils_1.BufferWriter.withCapacity(
300 8 * this.ins.length,
301 );
302 values.forEach(value => bufferWriter.writeUInt64(value));
303 hashAmounts = bcrypto.sha256(bufferWriter.end());
304 bufferWriter = bufferutils_1.BufferWriter.withCapacity(
305 prevOutScripts.map(varSliceSize).reduce((a, b) => a + b),
306 );
307 prevOutScripts.forEach(prevOutScript =>
308 bufferWriter.writeVarSlice(prevOutScript),
309 );
310 hashScriptPubKeys = bcrypto.sha256(bufferWriter.end());
311 bufferWriter = bufferutils_1.BufferWriter.withCapacity(
312 4 * this.ins.length,
313 );
314 this.ins.forEach(txIn => bufferWriter.writeUInt32(txIn.sequence));
315 hashSequences = bcrypto.sha256(bufferWriter.end());
316 }
317 if (!(isNone || isSingle)) {
318 const txOutsSize = this.outs
319 .map(output => 8 + varSliceSize(output.script))
320 .reduce((a, b) => a + b);
321 const bufferWriter = bufferutils_1.BufferWriter.withCapacity(txOutsSize);
322 this.outs.forEach(out => {
323 bufferWriter.writeUInt64(out.value);
324 bufferWriter.writeVarSlice(out.script);
325 });
326 hashOutputs = bcrypto.sha256(bufferWriter.end());
327 } else if (isSingle && inIndex < this.outs.length) {
328 const output = this.outs[inIndex];
329 const bufferWriter = bufferutils_1.BufferWriter.withCapacity(
330 8 + varSliceSize(output.script),
331 );
332 bufferWriter.writeUInt64(output.value);
333 bufferWriter.writeVarSlice(output.script);
334 hashOutputs = bcrypto.sha256(bufferWriter.end());
335 }
336 const spendType = (leafHash ? 2 : 0) + (annex ? 1 : 0);
337 // Length calculation from:
338 // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-14
339 // With extension from:
340 // https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#signature-validation
341 const sigMsgSize =
342 174 -
343 (isAnyoneCanPay ? 49 : 0) -
344 (isNone ? 32 : 0) +
345 (annex ? 32 : 0) +
346 (leafHash ? 37 : 0);
347 const sigMsgWriter = bufferutils_1.BufferWriter.withCapacity(sigMsgSize);
348 sigMsgWriter.writeUInt8(hashType);
349 // Transaction
350 sigMsgWriter.writeInt32(this.version);
351 sigMsgWriter.writeUInt32(this.locktime);
352 sigMsgWriter.writeSlice(hashPrevouts);
353 sigMsgWriter.writeSlice(hashAmounts);
354 sigMsgWriter.writeSlice(hashScriptPubKeys);
355 sigMsgWriter.writeSlice(hashSequences);
356 if (!(isNone || isSingle)) {
357 sigMsgWriter.writeSlice(hashOutputs);
358 }
359 // Input
360 sigMsgWriter.writeUInt8(spendType);
361 if (isAnyoneCanPay) {
362 const input = this.ins[inIndex];
363 sigMsgWriter.writeSlice(input.hash);
364 sigMsgWriter.writeUInt32(input.index);
365 sigMsgWriter.writeUInt64(values[inIndex]);
366 sigMsgWriter.writeVarSlice(prevOutScripts[inIndex]);
367 sigMsgWriter.writeUInt32(input.sequence);
368 } else {
369 sigMsgWriter.writeUInt32(inIndex);
370 }
371 if (annex) {
372 const bufferWriter = bufferutils_1.BufferWriter.withCapacity(
373 varSliceSize(annex),
374 );
375 bufferWriter.writeVarSlice(annex);
376 sigMsgWriter.writeSlice(bcrypto.sha256(bufferWriter.end()));
377 }
378 // Output
379 if (isSingle) {
380 sigMsgWriter.writeSlice(hashOutputs);
381 }
382 // BIP342 extension
383 if (leafHash) {
384 sigMsgWriter.writeSlice(leafHash);
385 sigMsgWriter.writeUInt8(0);
386 sigMsgWriter.writeUInt32(0xffffffff);
387 }
388 // Extra zero byte because:
389 // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-19
390 return bcrypto.taggedHash(
391 'TapSighash',
392 Buffer.concat([Buffer.from([0x00]), sigMsgWriter.end()]),
393 );
394 }
395 hashForWitnessV0(inIndex, prevOutScript, value, hashType) {
396 typeforce(
397 types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32),
398 arguments,
399 );
400 let tbuffer = Buffer.from([]);
401 let bufferWriter;
402 let hashOutputs = ZERO;
403 let hashPrevouts = ZERO;
404 let hashSequence = ZERO;
405 if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) {
406 tbuffer = Buffer.allocUnsafe(36 * this.ins.length);
407 bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0);
408 this.ins.forEach(txIn => {
409 bufferWriter.writeSlice(txIn.hash);
410 bufferWriter.writeUInt32(txIn.index);
411 });
412 hashPrevouts = bcrypto.hash256(tbuffer);
413 }
414 if (
415 !(hashType & Transaction.SIGHASH_ANYONECANPAY) &&
416 (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
417 (hashType & 0x1f) !== Transaction.SIGHASH_NONE
418 ) {
419 tbuffer = Buffer.allocUnsafe(4 * this.ins.length);
420 bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0);
421 this.ins.forEach(txIn => {
422 bufferWriter.writeUInt32(txIn.sequence);
423 });
424 hashSequence = bcrypto.hash256(tbuffer);
425 }
426 if (
427 (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE &&
428 (hashType & 0x1f) !== Transaction.SIGHASH_NONE
429 ) {
430 const txOutsSize = this.outs.reduce((sum, output) => {
431 return sum + 8 + varSliceSize(output.script);
432 }, 0);
433 tbuffer = Buffer.allocUnsafe(txOutsSize);
434 bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0);
435 this.outs.forEach(out => {
436 bufferWriter.writeUInt64(out.value);
437 bufferWriter.writeVarSlice(out.script);
438 });
439 hashOutputs = bcrypto.hash256(tbuffer);
440 } else if (
441 (hashType & 0x1f) === Transaction.SIGHASH_SINGLE &&
442 inIndex < this.outs.length
443 ) {
444 const output = this.outs[inIndex];
445 tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script));
446 bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0);
447 bufferWriter.writeUInt64(output.value);
448 bufferWriter.writeVarSlice(output.script);
449 hashOutputs = bcrypto.hash256(tbuffer);
450 }
451 tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript));
452 bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0);
453 const input = this.ins[inIndex];
454 bufferWriter.writeInt32(this.version);
455 bufferWriter.writeSlice(hashPrevouts);
456 bufferWriter.writeSlice(hashSequence);
457 bufferWriter.writeSlice(input.hash);
458 bufferWriter.writeUInt32(input.index);
459 bufferWriter.writeVarSlice(prevOutScript);
460 bufferWriter.writeUInt64(value);
461 bufferWriter.writeUInt32(input.sequence);
462 bufferWriter.writeSlice(hashOutputs);
463 bufferWriter.writeUInt32(this.locktime);
464 bufferWriter.writeUInt32(hashType);
465 return bcrypto.hash256(tbuffer);
466 }
467 getHash(forWitness) {
468 // wtxid for coinbase is always 32 bytes of 0x00
469 if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0);
470 return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness));
471 }
472 getId() {
473 // transaction hash's are displayed in reverse order
474 return (0, bufferutils_1.reverseBuffer)(this.getHash(false)).toString(
475 'hex',
476 );
477 }
478 toBuffer(buffer, initialOffset) {
479 return this.__toBuffer(buffer, initialOffset, true);
480 }
481 toHex() {
482 return this.toBuffer(undefined, undefined).toString('hex');
483 }
484 setInputScript(index, scriptSig) {
485 typeforce(types.tuple(types.Number, types.Buffer), arguments);
486 this.ins[index].script = scriptSig;
487 }
488 setWitness(index, witness) {
489 typeforce(types.tuple(types.Number, [types.Buffer]), arguments);
490 this.ins[index].witness = witness;
491 }
492 __toBuffer(buffer, initialOffset, _ALLOW_WITNESS = false) {
493 if (!buffer) buffer = Buffer.allocUnsafe(this.byteLength(_ALLOW_WITNESS));
494 const bufferWriter = new bufferutils_1.BufferWriter(
495 buffer,
496 initialOffset || 0,
497 );
498 bufferWriter.writeInt32(this.version);
499 const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
500 if (hasWitnesses) {
501 bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER);
502 bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG);
503 }
504 bufferWriter.writeVarInt(this.ins.length);
505 this.ins.forEach(txIn => {
506 bufferWriter.writeSlice(txIn.hash);
507 bufferWriter.writeUInt32(txIn.index);
508 bufferWriter.writeVarSlice(txIn.script);
509 bufferWriter.writeUInt32(txIn.sequence);
510 });
511 bufferWriter.writeVarInt(this.outs.length);
512 this.outs.forEach(txOut => {
513 if (isOutput(txOut)) {
514 bufferWriter.writeUInt64(txOut.value);
515 } else {
516 bufferWriter.writeSlice(txOut.valueBuffer);
517 }
518 bufferWriter.writeVarSlice(txOut.script);
519 });
520 if (hasWitnesses) {
521 this.ins.forEach(input => {
522 bufferWriter.writeVector(input.witness);
523 });
524 }
525 bufferWriter.writeUInt32(this.locktime);
526 // avoid slicing unless necessary
527 if (initialOffset !== undefined)
528 return buffer.slice(initialOffset, bufferWriter.offset);
529 return buffer;
530 }
531}
532exports.Transaction = Transaction;
533Transaction.DEFAULT_SEQUENCE = 0xffffffff;
534Transaction.SIGHASH_DEFAULT = 0x00;
535Transaction.SIGHASH_ALL = 0x01;
536Transaction.SIGHASH_NONE = 0x02;
537Transaction.SIGHASH_SINGLE = 0x03;
538Transaction.SIGHASH_ANYONECANPAY = 0x80;
539Transaction.SIGHASH_OUTPUT_MASK = 0x03;
540Transaction.SIGHASH_INPUT_MASK = 0x80;
541Transaction.ADVANCED_TRANSACTION_MARKER = 0x00;
542Transaction.ADVANCED_TRANSACTION_FLAG = 0x01;