diff --git a/src/transaction.js b/src/transaction.js index cfd31fe..c6c847a 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,11 +1,11 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const bcrypto = require("./crypto"); -const bscript = require("./script"); -const types = require("./types"); const bufferutils = require("./bufferutils"); const bufferutils_1 = require("./bufferutils"); +const bcrypto = require("./crypto"); +const bscript = require("./script"); const script_1 = require("./script"); +const types = require("./types"); const typeforce = require('typeforce'); const varuint = require('varuint-bitcoin'); function varSliceSize(someScript) { @@ -38,7 +38,7 @@ class Transaction { this.ins = []; this.outs = []; } - static fromBuffer(buffer, __noStrict) { + static fromBuffer(buffer, _NO_STRICT) { let offset = 0; function readSlice(n) { offset += n; @@ -70,7 +70,7 @@ class Transaction { function readVector() { const count = readVarInt(); const vector = []; - for (var i = 0; i < count; i++) + for (let i = 0; i < count; i++) vector.push(readVarSlice()); return vector; } @@ -85,7 +85,7 @@ class Transaction { hasWitnesses = true; } const vinLen = readVarInt(); - for (var i = 0; i < vinLen; ++i) { + for (let i = 0; i < vinLen; ++i) { tx.ins.push({ hash: readSlice(32), index: readUInt32(), @@ -95,14 +95,14 @@ class Transaction { }); } const voutLen = readVarInt(); - for (i = 0; i < voutLen; ++i) { + for (let i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), script: readVarSlice(), }); } if (hasWitnesses) { - for (i = 0; i < vinLen; ++i) { + for (let i = 0; i < vinLen; ++i) { tx.ins[i].witness = readVector(); } // was this pointless? @@ -110,7 +110,7 @@ class Transaction { throw new Error('Transaction has superfluous witness data'); } tx.locktime = readUInt32(); - if (__noStrict) + if (_NO_STRICT) return tx; if (offset !== buffer.length) throw new Error('Transaction has unexpected data'); @@ -121,7 +121,7 @@ class Transaction { } static isCoinbaseHash(buffer) { typeforce(types.Hash256bit, buffer); - for (var i = 0; i < 32; ++i) { + for (let i = 0; i < 32; ++i) { if (buffer[i] !== 0) return false; } @@ -137,8 +137,8 @@ class Transaction { } // Add the input and return the input's index return (this.ins.push({ - hash: hash, - index: index, + hash, + index, script: scriptSig || EMPTY_SCRIPT, sequence: sequence, witness: EMPTY_WITNESS, @@ -149,7 +149,7 @@ class Transaction { // Add the output and return the output's index return (this.outs.push({ script: scriptPubKey, - value: value, + value, }) - 1); } hasWitnesses() { @@ -168,23 +168,6 @@ class Transaction { byteLength() { return this.__byteLength(true); } - __byteLength(__allowWitness) { - const hasWitnesses = __allowWitness && this.hasWitnesses(); - return ((hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + - this.ins.reduce((sum, input) => { - return sum + 40 + varSliceSize(input.script); - }, 0) + - this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script); - }, 0) + - (hasWitnesses - ? this.ins.reduce((sum, input) => { - return sum + vectorSize(input.witness); - }, 0) - : 0)); - } clone() { const newTx = new Transaction(); newTx.version = this.version; @@ -242,7 +225,7 @@ class Transaction { // truncate outputs after txTmp.outs.length = inIndex + 1; // "blank" outputs before - for (var i = 0; i < inIndex; i++) { + for (let i = 0; i < inIndex; i++) { txTmp.outs[i] = BLANK_OUTPUT; } // ignore sequence numbers (except at inIndex) @@ -365,9 +348,37 @@ class Transaction { toBuffer(buffer, initialOffset) { return this.__toBuffer(buffer, initialOffset, true); } - __toBuffer(buffer, initialOffset, __allowWitness) { + toHex() { + return this.toBuffer(undefined, undefined).toString('hex'); + } + setInputScript(index, scriptSig) { + typeforce(types.tuple(types.Number, types.Buffer), arguments); + this.ins[index].script = scriptSig; + } + setWitness(index, witness) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments); + this.ins[index].witness = witness; + } + __byteLength(_ALLOW_WITNESS) { + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); + return ((hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => { + return sum + 40 + varSliceSize(input.script); + }, 0) + + this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script); + }, 0) + + (hasWitnesses + ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness); + }, 0) + : 0)); + } + __toBuffer(buffer, initialOffset, _ALLOW_WITNESS) { if (!buffer) - buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)); + buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS)); let offset = initialOffset || 0; function writeSlice(slice) { offset += slice.copy(buffer, offset); @@ -397,7 +408,7 @@ class Transaction { vector.forEach(writeVarSlice); } writeInt32(this.version); - const hasWitnesses = __allowWitness && this.hasWitnesses(); + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); if (hasWitnesses) { writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); @@ -430,17 +441,6 @@ class Transaction { return buffer.slice(initialOffset, offset); return buffer; } - toHex() { - return this.toBuffer(undefined, undefined).toString('hex'); - } - setInputScript(index, scriptSig) { - typeforce(types.tuple(types.Number, types.Buffer), arguments); - this.ins[index].script = scriptSig; - } - setWitness(index, witness) { - typeforce(types.tuple(types.Number, [types.Buffer]), arguments); - this.ins[index].witness = witness; - } } Transaction.DEFAULT_SEQUENCE = 0xffffffff; Transaction.SIGHASH_ALL = 0x01; diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index a456a32..995f1d7 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -1,9 +1,9 @@ -import * as bcrypto from './crypto'; -import * as bscript from './script'; -import * as types from './types'; import * as bufferutils from './bufferutils'; import { reverseBuffer } from './bufferutils'; +import * as bcrypto from './crypto'; +import * as bscript from './script'; import { OPS as opcodes } from './script'; +import * as types from './types'; const typeforce = require('typeforce'); const varuint = require('varuint-bitcoin'); @@ -14,7 +14,7 @@ function varSliceSize(someScript: Buffer): number { return varuint.encodingLength(length) + length; } -function vectorSize(someVector: Array): number { +function vectorSize(someVector: Buffer[]): number { const length = someVector.length; return ( @@ -26,7 +26,7 @@ function vectorSize(someVector: Array): number { } const EMPTY_SCRIPT: Buffer = Buffer.allocUnsafe(0); -const EMPTY_WITNESS: Array = []; +const EMPTY_WITNESS: Buffer[] = []; const ZERO: Buffer = Buffer.from( '0000000000000000000000000000000000000000000000000000000000000000', 'hex', @@ -42,33 +42,30 @@ const BLANK_OUTPUT: BlankOutput = { }; function isOutput(out: Output | BlankOutput): out is Output { - return (out).value !== undefined; + return (out as Output).value !== undefined; } -export type BlankOutput = { +export interface BlankOutput { script: Buffer; valueBuffer: Buffer; -}; +} -export type Output = { +export interface Output { script: Buffer; value: number; -}; +} -export type Input = { +type OpenOutput = Output | BlankOutput; + +export interface Input { hash: Buffer; index: number; script: Buffer; sequence: number; - witness: Array; -}; + witness: Buffer[]; +} export class Transaction { - version: number; - locktime: number; - ins: Array; - outs: Array; - static readonly DEFAULT_SEQUENCE = 0xffffffff; static readonly SIGHASH_ALL = 0x01; static readonly SIGHASH_NONE = 0x02; @@ -77,14 +74,7 @@ export class Transaction { static readonly ADVANCED_TRANSACTION_MARKER = 0x00; static readonly ADVANCED_TRANSACTION_FLAG = 0x01; - constructor() { - this.version = 1; - this.locktime = 0; - this.ins = []; - this.outs = []; - } - - static fromBuffer(buffer: Buffer, __noStrict?: boolean): Transaction { + static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction { let offset: number = 0; function readSlice(n: number): Buffer { @@ -120,10 +110,10 @@ export class Transaction { return readSlice(readVarInt()); } - function readVector(): Array { + function readVector(): Buffer[] { const count = readVarInt(); - const vector: Array = []; - for (var i = 0; i < count; i++) vector.push(readVarSlice()); + const vector: Buffer[] = []; + for (let i = 0; i < count; i++) vector.push(readVarSlice()); return vector; } @@ -143,7 +133,7 @@ export class Transaction { } const vinLen = readVarInt(); - for (var i = 0; i < vinLen; ++i) { + for (let i = 0; i < vinLen; ++i) { tx.ins.push({ hash: readSlice(32), index: readUInt32(), @@ -154,7 +144,7 @@ export class Transaction { } const voutLen = readVarInt(); - for (i = 0; i < voutLen; ++i) { + for (let i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), script: readVarSlice(), @@ -162,7 +152,7 @@ export class Transaction { } if (hasWitnesses) { - for (i = 0; i < vinLen; ++i) { + for (let i = 0; i < vinLen; ++i) { tx.ins[i].witness = readVector(); } @@ -173,7 +163,7 @@ export class Transaction { tx.locktime = readUInt32(); - if (__noStrict) return tx; + if (_NO_STRICT) return tx; if (offset !== buffer.length) throw new Error('Transaction has unexpected data'); @@ -186,12 +176,24 @@ export class Transaction { static isCoinbaseHash(buffer: Buffer): boolean { typeforce(types.Hash256bit, buffer); - for (var i = 0; i < 32; ++i) { + for (let i = 0; i < 32; ++i) { if (buffer[i] !== 0) return false; } return true; } + version: number; + locktime: number; + ins: Input[]; + outs: OpenOutput[]; + + constructor() { + this.version = 1; + this.locktime = 0; + this.ins = []; + this.outs = []; + } + isCoinbase(): boolean { return ( this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) @@ -221,10 +223,10 @@ export class Transaction { // Add the input and return the input's index return ( this.ins.push({ - hash: hash, - index: index, + hash, + index, script: scriptSig || EMPTY_SCRIPT, - sequence: sequence, + sequence: sequence as number, witness: EMPTY_WITNESS, }) - 1 ); @@ -237,7 +239,7 @@ export class Transaction { return ( this.outs.push({ script: scriptPubKey, - value: value, + value, }) - 1 ); } @@ -262,27 +264,6 @@ export class Transaction { return this.__byteLength(true); } - private __byteLength(__allowWitness: boolean): number { - const hasWitnesses = __allowWitness && this.hasWitnesses(); - - return ( - (hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + - this.ins.reduce((sum, input) => { - return sum + 40 + varSliceSize(input.script); - }, 0) + - this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script); - }, 0) + - (hasWitnesses - ? this.ins.reduce((sum, input) => { - return sum + vectorSize(input.witness); - }, 0) - : 0) - ); - } - clone(): Transaction { const newTx = new Transaction(); newTx.version = this.version; @@ -301,7 +282,7 @@ export class Transaction { newTx.outs = this.outs.map(txOut => { return { script: txOut.script, - value: (txOut).value, + value: (txOut as Output).value, }; }); @@ -358,7 +339,7 @@ export class Transaction { txTmp.outs.length = inIndex + 1; // "blank" outputs before - for (var i = 0; i < inIndex; i++) { + for (let i = 0; i < inIndex; i++) { txTmp.outs[i] = BLANK_OUTPUT; } @@ -471,7 +452,7 @@ export class Transaction { toffset = 0; this.outs.forEach(out => { - writeUInt64((out).value); + writeUInt64((out as Output).value); writeVarSlice(out.script); }); @@ -484,7 +465,7 @@ export class Transaction { tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); toffset = 0; - writeUInt64((output).value); + writeUInt64((output as Output).value); writeVarSlice(output.script); hashOutputs = bcrypto.hash256(tbuffer); @@ -523,13 +504,50 @@ export class Transaction { return this.__toBuffer(buffer, initialOffset, true); } + toHex() { + return this.toBuffer(undefined, undefined).toString('hex'); + } + + setInputScript(index: number, scriptSig: Buffer) { + typeforce(types.tuple(types.Number, types.Buffer), arguments); + + this.ins[index].script = scriptSig; + } + + setWitness(index: number, witness: Buffer[]) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments); + + this.ins[index].witness = witness; + } + + private __byteLength(_ALLOW_WITNESS: boolean): number { + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); + + return ( + (hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => { + return sum + 40 + varSliceSize(input.script); + }, 0) + + this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script); + }, 0) + + (hasWitnesses + ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness); + }, 0) + : 0) + ); + } + private __toBuffer( buffer?: Buffer, initialOffset?: number, - __allowWitness?: boolean, + _ALLOW_WITNESS?: boolean, ): Buffer { if (!buffer) - buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness!)); + buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS!)) as Buffer; let offset = initialOffset || 0; @@ -563,14 +581,14 @@ export class Transaction { writeSlice(slice); } - function writeVector(vector: Array) { + function writeVector(vector: Buffer[]) { writeVarInt(vector.length); vector.forEach(writeVarSlice); } writeInt32(this.version); - const hasWitnesses = __allowWitness && this.hasWitnesses(); + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); if (hasWitnesses) { writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); @@ -609,20 +627,4 @@ export class Transaction { if (initialOffset !== undefined) return buffer.slice(initialOffset, offset); return buffer; } - - toHex() { - return this.toBuffer(undefined, undefined).toString('hex'); - } - - setInputScript(index: number, scriptSig: Buffer) { - typeforce(types.tuple(types.Number, types.Buffer), arguments); - - this.ins[index].script = scriptSig; - } - - setWitness(index: number, witness: Array) { - typeforce(types.tuple(types.Number, [types.Buffer]), arguments); - - this.ins[index].witness = witness; - } } diff --git a/types/transaction.d.ts b/types/transaction.d.ts index 60bbf45..4f8a2b9 100644 --- a/types/transaction.d.ts +++ b/types/transaction.d.ts @@ -1,24 +1,21 @@ /// -export declare type BlankOutput = { +export interface BlankOutput { script: Buffer; valueBuffer: Buffer; -}; -export declare type Output = { +} +export interface Output { script: Buffer; value: number; -}; -export declare type Input = { +} +declare type OpenOutput = Output | BlankOutput; +export interface Input { hash: Buffer; index: number; script: Buffer; sequence: number; - witness: Array; -}; + witness: Buffer[]; +} export declare class Transaction { - version: number; - locktime: number; - ins: Array; - outs: Array; static readonly DEFAULT_SEQUENCE = 4294967295; static readonly SIGHASH_ALL = 1; static readonly SIGHASH_NONE = 2; @@ -26,10 +23,14 @@ export declare class Transaction { static readonly SIGHASH_ANYONECANPAY = 128; static readonly ADVANCED_TRANSACTION_MARKER = 0; static readonly ADVANCED_TRANSACTION_FLAG = 1; - constructor(); - static fromBuffer(buffer: Buffer, __noStrict?: boolean): Transaction; + static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction; static fromHex(hex: string): Transaction; static isCoinbaseHash(buffer: Buffer): boolean; + version: number; + locktime: number; + ins: Input[]; + outs: OpenOutput[]; + constructor(); isCoinbase(): boolean; addInput(hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer): number; addOutput(scriptPubKey: Buffer, value: number): number; @@ -37,7 +38,6 @@ export declare class Transaction { weight(): number; virtualSize(): number; byteLength(): number; - private __byteLength; clone(): Transaction; /** * Hash transaction for signing a specific input. @@ -52,8 +52,10 @@ export declare class Transaction { getHash(forWitness?: boolean): Buffer; getId(): string; toBuffer(buffer?: Buffer, initialOffset?: number): Buffer; - private __toBuffer; toHex(): string; setInputScript(index: number, scriptSig: Buffer): void; - setWitness(index: number, witness: Array): void; + setWitness(index: number, witness: Buffer[]): void; + private __byteLength; + private __toBuffer; } +export {};