UNPKG

14.5 kBJavaScriptView Raw
1'use strict';
2Object.defineProperty(exports, '__esModule', { value: true });
3const bufferutils = require('./bufferutils');
4const bufferutils_1 = require('./bufferutils');
5const bcrypto = require('./crypto');
6const bscript = require('./script');
7const script_1 = require('./script');
8const types = require('./types');
9const typeforce = require('typeforce');
10const varuint = require('varuint-bitcoin');
11function varSliceSize(someScript) {
12 const length = someScript.length;
13 return varuint.encodingLength(length) + length;
14}
15function 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}
24const EMPTY_SCRIPT = Buffer.allocUnsafe(0);
25const EMPTY_WITNESS = [];
26const ZERO = Buffer.from(
27 '0000000000000000000000000000000000000000000000000000000000000000',
28 'hex',
29);
30const ONE = Buffer.from(
31 '0000000000000000000000000000000000000000000000000000000000000001',
32 'hex',
33);
34const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex');
35const BLANK_OUTPUT = {
36 script: EMPTY_SCRIPT,
37 valueBuffer: VALUE_UINT64_MAX,
38};
39function isOutput(out) {
40 return out.value !== undefined;
41}
42class 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 // was this pointless?
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 // Add the input and return the input's index
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 // Add the output and return the output's index
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 * Hash transaction for signing a specific input.
215 *
216 * Bitcoin uses a different hash for each signed transaction input.
217 * This method copies the transaction, makes the necessary changes based on the
218 * hashType, and then hashes the result.
219 * This hash can then be used to sign the provided transaction input.
220 */
221 hashForSignature(inIndex, prevOutScript, hashType) {
222 typeforce(
223 types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number),
224 arguments,
225 );
226 // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
227 if (inIndex >= this.ins.length) return ONE;
228 // ignore OP_CODESEPARATOR
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 // SIGHASH_NONE: ignore all outputs? (wildcard payee)
236 if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
237 txTmp.outs = [];
238 // ignore sequence numbers (except at inIndex)
239 txTmp.ins.forEach((input, i) => {
240 if (i === inIndex) return;
241 input.sequence = 0;
242 });
243 // SIGHASH_SINGLE: ignore all outputs, except at the same index?
244 } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
245 // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
246 if (inIndex >= this.outs.length) return ONE;
247 // truncate outputs after
248 txTmp.outs.length = inIndex + 1;
249 // "blank" outputs before
250 for (let i = 0; i < inIndex; i++) {
251 txTmp.outs[i] = BLANK_OUTPUT;
252 }
253 // ignore sequence numbers (except at inIndex)
254 txTmp.ins.forEach((input, y) => {
255 if (y === inIndex) return;
256 input.sequence = 0;
257 });
258 }
259 // SIGHASH_ANYONECANPAY: ignore inputs entirely?
260 if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
261 txTmp.ins = [txTmp.ins[inIndex]];
262 txTmp.ins[0].script = ourScript;
263 // SIGHASH_ALL: only ignore input scripts
264 } else {
265 // "blank" others input scripts
266 txTmp.ins.forEach(input => {
267 input.script = EMPTY_SCRIPT;
268 });
269 txTmp.ins[inIndex].script = ourScript;
270 }
271 // serialize and hash
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 // wtxid for coinbase is always 32 bytes of 0x00
368 if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0);
369 return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness));
370 }
371 getId() {
372 // transaction hash's are displayed in reverse order
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 // avoid slicing unless necessary
467 if (initialOffset !== undefined) return buffer.slice(initialOffset, offset);
468 return buffer;
469 }
470}
471Transaction.DEFAULT_SEQUENCE = 0xffffffff;
472Transaction.SIGHASH_ALL = 0x01;
473Transaction.SIGHASH_NONE = 0x02;
474Transaction.SIGHASH_SINGLE = 0x03;
475Transaction.SIGHASH_ANYONECANPAY = 0x80;
476Transaction.ADVANCED_TRANSACTION_MARKER = 0x00;
477Transaction.ADVANCED_TRANSACTION_FLAG = 0x01;
478exports.Transaction = Transaction;